зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 5df8f9f0a5b9 (bug 1370430) to see if it unbreaks builds a=backout CLOSED TREE
MozReview-Commit-ID: G2snPHLTZg5 --HG-- rename : third_party/rust/euclid-0.13.0/src/matrix2d.rs => third_party/rust/euclid/src/matrix2d.rs rename : third_party/rust/euclid-0.13.0/src/matrix4d.rs => third_party/rust/euclid/src/matrix4d.rs extra : amend_source : 796c79ec645c0d1d357d052585ef9da24ec887fe
This commit is contained in:
Родитель
b32bf15627
Коммит
c2afdb09bd
|
@ -79,4 +79,4 @@ to make sure that mozjs_sys also has its Cargo.lock file updated if needed, henc
|
|||
the need to run the cargo update command in js/src as well. Hopefully this will
|
||||
be resolved soon.
|
||||
|
||||
Latest Commit: 6752684fcc7402b0a5480e0b9f73152b2f9ed1e5
|
||||
Latest Commit: b2614e4eb58f9dee08b8c38f96bc3bac834c837b
|
||||
|
|
|
@ -17,20 +17,20 @@ app_units = "0.4"
|
|||
bincode = "1.0.0-alpha6"
|
||||
bit-set = "0.4"
|
||||
byteorder = "1.0"
|
||||
euclid = "0.14.4"
|
||||
euclid = "0.13"
|
||||
fnv = "1.0"
|
||||
gleam = "0.4.3"
|
||||
lazy_static = "0.2"
|
||||
log = "0.3"
|
||||
num-traits = "0.1.32"
|
||||
offscreen_gl_context = {version = "0.9.0", features = ["serde", "osmesa"], optional = true}
|
||||
offscreen_gl_context = {version = "0.8.0", features = ["serde", "osmesa"], optional = true}
|
||||
time = "0.1"
|
||||
rayon = {version = "0.7", features = ["unstable"]}
|
||||
webrender_traits = {path = "../webrender_traits"}
|
||||
bitflags = "0.7"
|
||||
gamma-lut = "0.2"
|
||||
thread_profiler = "0.1.1"
|
||||
plane-split = "0.5"
|
||||
plane-split = "0.4"
|
||||
|
||||
[dev-dependencies]
|
||||
angle = {git = "https://github.com/servo/angle", branch = "servo"}
|
||||
|
@ -46,3 +46,15 @@ dwrote = "0.3"
|
|||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-graphics = "0.7.0"
|
||||
core-text = "4.0"
|
||||
|
||||
[[example]]
|
||||
name = "basic"
|
||||
|
||||
[[example]]
|
||||
name = "blob"
|
||||
|
||||
[[example]]
|
||||
name = "scrolling"
|
||||
|
||||
[[example]]
|
||||
name = "yuv"
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
extern crate gleam;
|
||||
extern crate glutin;
|
||||
extern crate webrender;
|
||||
extern crate webrender_traits;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
#[path="common/boilerplate.rs"]
|
||||
mod boilerplate;
|
||||
|
||||
use boilerplate::HandyDandyRectBuilder;
|
||||
use std::sync::Mutex;
|
||||
use webrender_traits::*;
|
||||
|
||||
// This example creates a 100x100 white rect and allows the user to move it
|
||||
// around by using the arrow keys. It does this by using the animation API.
|
||||
|
||||
fn body(_api: &RenderApi,
|
||||
builder: &mut DisplayListBuilder,
|
||||
_pipeline_id: &PipelineId,
|
||||
_layout_size: &LayoutSize)
|
||||
{
|
||||
// Create a 100x100 stacking context with an animatable transform property.
|
||||
// Note the magic "42" we use as the animation key. That is used to update
|
||||
// the transform in the keyboard event handler code.
|
||||
let bounds = (0,0).to(100, 100);
|
||||
builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
|
||||
bounds,
|
||||
Some(PropertyBinding::Binding(PropertyBindingKey::new(42))),
|
||||
TransformStyle::Flat,
|
||||
None,
|
||||
webrender_traits::MixBlendMode::Normal,
|
||||
Vec::new());
|
||||
|
||||
// Fill it with a white rect
|
||||
let clip = builder.push_clip_region(&bounds, vec![], None);
|
||||
builder.push_rect(bounds,
|
||||
clip,
|
||||
ColorF::new(1.0, 1.0, 1.0, 1.0));
|
||||
|
||||
builder.pop_stacking_context();
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref TRANSFORM: Mutex<LayoutTransform> = Mutex::new(LayoutTransform::identity());
|
||||
}
|
||||
|
||||
fn event_handler(event: &glutin::Event,
|
||||
api: &RenderApi)
|
||||
{
|
||||
match *event {
|
||||
glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
|
||||
let offset = match key {
|
||||
glutin::VirtualKeyCode::Down => (0.0, 10.0),
|
||||
glutin::VirtualKeyCode::Up => (0.0, -10.0),
|
||||
glutin::VirtualKeyCode::Right => (10.0, 0.0),
|
||||
glutin::VirtualKeyCode::Left => (-10.0, 0.0),
|
||||
_ => return,
|
||||
};
|
||||
// Update the transform based on the keyboard input and push it to
|
||||
// webrender using the generate_frame API. This will recomposite with
|
||||
// the updated transform.
|
||||
let new_transform = TRANSFORM.lock().unwrap().post_translate(LayoutVector3D::new(offset.0, offset.1, 0.0));
|
||||
api.generate_frame(Some(DynamicProperties {
|
||||
transforms: vec![
|
||||
PropertyValue {
|
||||
key: PropertyBindingKey::new(42),
|
||||
value: new_transform,
|
||||
},
|
||||
],
|
||||
floats: vec![],
|
||||
}));
|
||||
*TRANSFORM.lock().unwrap() = new_transform;
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
boilerplate::main_wrapper(body, event_handler, None);
|
||||
}
|
|
@ -21,7 +21,6 @@ use webrender_traits::{ClipRegionToken, ColorF, DisplayListBuilder, Epoch, Glyph
|
|||
use webrender_traits::{DeviceIntPoint, DeviceUintSize, LayoutPoint, LayoutRect, LayoutSize};
|
||||
use webrender_traits::{ImageData, ImageDescriptor, ImageFormat};
|
||||
use webrender_traits::{PipelineId, RenderApi, TransformStyle, BoxShadowClipMode};
|
||||
use euclid::vec2;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Gesture {
|
||||
|
@ -382,7 +381,7 @@ fn main() {
|
|||
let rect = LayoutRect::new(LayoutPoint::new(0.0, 0.0), LayoutSize::new(0.0, 0.0));
|
||||
let simple_box_bounds = LayoutRect::new(LayoutPoint::new(20.0, 200.0),
|
||||
LayoutSize::new(50.0, 50.0));
|
||||
let offset = vec2(10.0, 10.0);
|
||||
let offset = LayoutPoint::new(10.0, 10.0);
|
||||
let color = ColorF::new(1.0, 1.0, 1.0, 1.0);
|
||||
let blur_radius = 0.0;
|
||||
let spread_radius = 0.0;
|
||||
|
|
|
@ -10,36 +10,37 @@ extern crate webrender;
|
|||
extern crate webrender_traits;
|
||||
extern crate rayon;
|
||||
|
||||
#[path="common/boilerplate.rs"]
|
||||
mod boilerplate;
|
||||
|
||||
use boilerplate::HandyDandyRectBuilder;
|
||||
use gleam::gl;
|
||||
use rayon::ThreadPool;
|
||||
use rayon::Configuration as ThreadPoolConfig;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::sync::Arc;
|
||||
use std::sync::mpsc::{channel, Sender, Receiver};
|
||||
use webrender_traits as wt;
|
||||
use webrender_traits::{BlobImageData, BlobImageDescriptor, BlobImageError, BlobImageRenderer, BlobImageRequest};
|
||||
use webrender_traits::{BlobImageResult, TileOffset, ImageStore, ColorF, ColorU, Epoch};
|
||||
use webrender_traits::{DeviceUintSize, DeviceUintRect, LayoutPoint, LayoutRect, LayoutSize};
|
||||
use webrender_traits::{ImageData, ImageDescriptor, ImageFormat, ImageRendering, ImageKey, TileSize};
|
||||
use webrender_traits::{PipelineId, RasterizedBlobImage, TransformStyle};
|
||||
|
||||
// This example shows how to implement a very basic BlobImageRenderer that can only render
|
||||
// a checkerboard pattern.
|
||||
|
||||
// The deserialized command list internally used by this example is just a color.
|
||||
type ImageRenderingCommands = wt::ColorU;
|
||||
type ImageRenderingCommands = ColorU;
|
||||
|
||||
// Serialize/deserialze the blob.
|
||||
// Ror real usecases you should probably use serde rather than doing it by hand.
|
||||
|
||||
fn serialize_blob(color: wt::ColorU) -> Vec<u8> {
|
||||
fn serialize_blob(color: ColorU) -> Vec<u8> {
|
||||
vec![color.r, color.g, color.b, color.a]
|
||||
}
|
||||
|
||||
fn deserialize_blob(blob: &[u8]) -> Result<ImageRenderingCommands, ()> {
|
||||
let mut iter = blob.iter();
|
||||
return match (iter.next(), iter.next(), iter.next(), iter.next()) {
|
||||
(Some(&r), Some(&g), Some(&b), Some(&a)) => Ok(wt::ColorU::new(r, g, b, a)),
|
||||
(Some(&a), None, None, None) => Ok(wt::ColorU::new(a, a, a, a)),
|
||||
(Some(&r), Some(&g), Some(&b), Some(&a)) => Ok(ColorU::new(r, g, b, a)),
|
||||
(Some(&a), None, None, None) => Ok(ColorU::new(a, a, a, a)),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
@ -48,9 +49,9 @@ fn deserialize_blob(blob: &[u8]) -> Result<ImageRenderingCommands, ()> {
|
|||
// actual image data.
|
||||
fn render_blob(
|
||||
commands: Arc<ImageRenderingCommands>,
|
||||
descriptor: &wt::BlobImageDescriptor,
|
||||
tile: Option<wt::TileOffset>,
|
||||
) -> wt::BlobImageResult {
|
||||
descriptor: &BlobImageDescriptor,
|
||||
tile: Option<TileOffset>,
|
||||
) -> BlobImageResult {
|
||||
let color = *commands;
|
||||
|
||||
// Allocate storage for the result. Right now the resource cache expects the
|
||||
|
@ -77,17 +78,17 @@ fn render_blob(
|
|||
let tc = if tile_checker { 0 } else { (1 - checker) * 40 };
|
||||
|
||||
match descriptor.format {
|
||||
wt::ImageFormat::RGBA8 => {
|
||||
ImageFormat::RGBA8 => {
|
||||
texels.push(color.b * checker + tc);
|
||||
texels.push(color.g * checker + tc);
|
||||
texels.push(color.r * checker + tc);
|
||||
texels.push(color.a * checker + tc);
|
||||
}
|
||||
wt::ImageFormat::A8 => {
|
||||
ImageFormat::A8 => {
|
||||
texels.push(color.a * checker + tc);
|
||||
}
|
||||
_ => {
|
||||
return Err(wt::BlobImageError::Other(format!(
|
||||
return Err(BlobImageError::Other(format!(
|
||||
"Usupported image format {:?}",
|
||||
descriptor.format
|
||||
)));
|
||||
|
@ -96,7 +97,7 @@ fn render_blob(
|
|||
}
|
||||
}
|
||||
|
||||
Ok(wt::RasterizedBlobImage {
|
||||
Ok(RasterizedBlobImage {
|
||||
data: texels,
|
||||
width: descriptor.width,
|
||||
height: descriptor.height,
|
||||
|
@ -111,18 +112,18 @@ struct CheckerboardRenderer {
|
|||
workers: Arc<ThreadPool>,
|
||||
|
||||
// the workers will use an mpsc channel to communicate the result.
|
||||
tx: Sender<(wt::BlobImageRequest, wt::BlobImageResult)>,
|
||||
rx: Receiver<(wt::BlobImageRequest, wt::BlobImageResult)>,
|
||||
tx: Sender<(BlobImageRequest, BlobImageResult)>,
|
||||
rx: Receiver<(BlobImageRequest, BlobImageResult)>,
|
||||
|
||||
// The deserialized drawing commands.
|
||||
// In this example we store them in Arcs. This isn't necessary since in this simplified
|
||||
// case the command list is a simple 32 bits value and would be cheap to clone before sending
|
||||
// to the workers. But in a more realistic scenario the commands would typically be bigger
|
||||
// and more expensive to clone, so let's pretend it is also the case here.
|
||||
image_cmds: HashMap<wt::ImageKey, Arc<ImageRenderingCommands>>,
|
||||
image_cmds: HashMap<ImageKey, Arc<ImageRenderingCommands>>,
|
||||
|
||||
// The images rendered in the current frame (not kept here between frames).
|
||||
rendered_images: HashMap<wt::BlobImageRequest, Option<wt::BlobImageResult>>,
|
||||
rendered_images: HashMap<BlobImageRequest, Option<BlobImageResult>>,
|
||||
}
|
||||
|
||||
impl CheckerboardRenderer {
|
||||
|
@ -138,26 +139,26 @@ impl CheckerboardRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
impl wt::BlobImageRenderer for CheckerboardRenderer {
|
||||
fn add(&mut self, key: wt::ImageKey, cmds: wt::BlobImageData, _: Option<wt::TileSize>) {
|
||||
impl BlobImageRenderer for CheckerboardRenderer {
|
||||
fn add(&mut self, key: ImageKey, cmds: BlobImageData, _: Option<TileSize>) {
|
||||
self.image_cmds.insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
|
||||
}
|
||||
|
||||
fn update(&mut self, key: wt::ImageKey, cmds: wt::BlobImageData) {
|
||||
fn update(&mut self, key: ImageKey, cmds: BlobImageData) {
|
||||
// Here, updating is just replacing the current version of the commands with
|
||||
// the new one (no incremental updates).
|
||||
self.image_cmds.insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
|
||||
}
|
||||
|
||||
fn delete(&mut self, key: wt::ImageKey) {
|
||||
fn delete(&mut self, key: ImageKey) {
|
||||
self.image_cmds.remove(&key);
|
||||
}
|
||||
|
||||
fn request(&mut self,
|
||||
resources: &wt::BlobImageResources,
|
||||
request: wt::BlobImageRequest,
|
||||
descriptor: &wt::BlobImageDescriptor,
|
||||
_dirty_rect: Option<wt::DeviceUintRect>) {
|
||||
request: BlobImageRequest,
|
||||
descriptor: &BlobImageDescriptor,
|
||||
_dirty_rect: Option<DeviceUintRect>,
|
||||
_images: &ImageStore) {
|
||||
// This method is where we kick off our rendering jobs.
|
||||
// It should avoid doing work on the calling thread as much as possible.
|
||||
// In this example we will use the thread pool to render individual tiles.
|
||||
|
@ -179,7 +180,7 @@ impl wt::BlobImageRenderer for CheckerboardRenderer {
|
|||
self.rendered_images.insert(request, None);
|
||||
}
|
||||
|
||||
fn resolve(&mut self, request: wt::BlobImageRequest) -> wt::BlobImageResult {
|
||||
fn resolve(&mut self, request: BlobImageRequest) -> BlobImageResult {
|
||||
// In this method we wait until the work is complete on the worker threads and
|
||||
// gather the results.
|
||||
|
||||
|
@ -187,7 +188,7 @@ impl wt::BlobImageRenderer for CheckerboardRenderer {
|
|||
// that we are looking for.
|
||||
match self.rendered_images.entry(request) {
|
||||
Entry::Vacant(_) => {
|
||||
return Err(wt::BlobImageError::InvalidKey);
|
||||
return Err(BlobImageError::InvalidKey);
|
||||
}
|
||||
Entry::Occupied(entry) => {
|
||||
// None means we haven't yet received the result.
|
||||
|
@ -208,70 +209,34 @@ impl wt::BlobImageRenderer for CheckerboardRenderer {
|
|||
}
|
||||
|
||||
// If we break out of the loop above it means the channel closed unexpectedly.
|
||||
Err(wt::BlobImageError::Other("Channel closed".into()))
|
||||
Err(BlobImageError::Other("Channel closed".into()))
|
||||
}
|
||||
fn delete_font(&mut self, font: wt::FontKey) {}
|
||||
}
|
||||
|
||||
fn body(api: &wt::RenderApi,
|
||||
builder: &mut wt::DisplayListBuilder,
|
||||
_pipeline_id: &wt::PipelineId,
|
||||
layout_size: &wt::LayoutSize)
|
||||
{
|
||||
let blob_img1 = api.generate_image_key();
|
||||
api.add_image(
|
||||
blob_img1,
|
||||
wt::ImageDescriptor::new(500, 500, wt::ImageFormat::RGBA8, true),
|
||||
wt::ImageData::new_blob_image(serialize_blob(wt::ColorU::new(50, 50, 150, 255))),
|
||||
Some(128),
|
||||
);
|
||||
|
||||
let blob_img2 = api.generate_image_key();
|
||||
api.add_image(
|
||||
blob_img2,
|
||||
wt::ImageDescriptor::new(200, 200, wt::ImageFormat::RGBA8, true),
|
||||
wt::ImageData::new_blob_image(serialize_blob(wt::ColorU::new(50, 150, 50, 255))),
|
||||
None,
|
||||
);
|
||||
|
||||
let bounds = wt::LayoutRect::new(wt::LayoutPoint::zero(), *layout_size);
|
||||
builder.push_stacking_context(wt::ScrollPolicy::Scrollable,
|
||||
bounds,
|
||||
None,
|
||||
wt::TransformStyle::Flat,
|
||||
None,
|
||||
wt::MixBlendMode::Normal,
|
||||
Vec::new());
|
||||
|
||||
let clip = builder.push_clip_region(&bounds, vec![], None);
|
||||
builder.push_image(
|
||||
(30, 30).by(500, 500),
|
||||
clip,
|
||||
wt::LayoutSize::new(500.0, 500.0),
|
||||
wt::LayoutSize::new(0.0, 0.0),
|
||||
wt::ImageRendering::Auto,
|
||||
blob_img1,
|
||||
);
|
||||
|
||||
let clip = builder.push_clip_region(&bounds, vec![], None);
|
||||
builder.push_image(
|
||||
(600, 600).by(200, 200),
|
||||
clip,
|
||||
wt::LayoutSize::new(200.0, 200.0),
|
||||
wt::LayoutSize::new(0.0, 0.0),
|
||||
wt::ImageRendering::Auto,
|
||||
blob_img2,
|
||||
);
|
||||
|
||||
builder.pop_stacking_context();
|
||||
}
|
||||
|
||||
fn event_handler(_event: &glutin::Event,
|
||||
_api: &wt::RenderApi)
|
||||
{
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let window = glutin::WindowBuilder::new()
|
||||
.with_title("WebRender Sample (BlobImageRenderer)")
|
||||
.with_multitouch()
|
||||
.with_gl(glutin::GlRequest::GlThenGles {
|
||||
opengl_version: (3, 2),
|
||||
opengles_version: (3, 0)
|
||||
})
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
unsafe {
|
||||
window.make_current().ok();
|
||||
}
|
||||
|
||||
let gl = match gl::GlType::default() {
|
||||
gl::GlType::Gl => unsafe { gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
|
||||
gl::GlType::Gles => unsafe { gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
|
||||
};
|
||||
|
||||
println!("OpenGL version {}", gl.get_string(gl::VERSION));
|
||||
|
||||
let (width, height) = window.get_inner_size_pixels().unwrap();
|
||||
|
||||
let worker_config = ThreadPoolConfig::new().thread_name(|idx|{
|
||||
format!("WebRender:Worker#{}", idx)
|
||||
});
|
||||
|
@ -279,12 +244,134 @@ fn main() {
|
|||
let workers = Arc::new(ThreadPool::new(worker_config).unwrap());
|
||||
|
||||
let opts = webrender::RendererOptions {
|
||||
debug: true,
|
||||
workers: Some(Arc::clone(&workers)),
|
||||
// Register our blob renderer, so that WebRender integrates it in the resource cache..
|
||||
// Share the same pool of worker threads between WebRender and our blob renderer.
|
||||
blob_image_renderer: Some(Box::new(CheckerboardRenderer::new(Arc::clone(&workers)))),
|
||||
device_pixel_ratio: window.hidpi_factor(),
|
||||
.. Default::default()
|
||||
};
|
||||
|
||||
boilerplate::main_wrapper(body, event_handler, Some(opts));
|
||||
let size = DeviceUintSize::new(width, height);
|
||||
let (mut renderer, sender) = webrender::renderer::Renderer::new(gl, opts, size).unwrap();
|
||||
let api = sender.create_api();
|
||||
|
||||
let notifier = Box::new(Notifier::new(window.create_window_proxy()));
|
||||
renderer.set_render_notifier(notifier);
|
||||
|
||||
let epoch = Epoch(0);
|
||||
let root_background_color = ColorF::new(0.2, 0.2, 0.2, 1.0);
|
||||
|
||||
let blob_img1 = api.generate_image_key();
|
||||
api.add_image(
|
||||
blob_img1,
|
||||
ImageDescriptor::new(500, 500, ImageFormat::RGBA8, true),
|
||||
ImageData::new_blob_image(serialize_blob(ColorU::new(50, 50, 150, 255))),
|
||||
Some(128),
|
||||
);
|
||||
|
||||
let blob_img2 = api.generate_image_key();
|
||||
api.add_image(
|
||||
blob_img2,
|
||||
ImageDescriptor::new(200, 200, ImageFormat::RGBA8, true),
|
||||
ImageData::new_blob_image(serialize_blob(ColorU::new(50, 150, 50, 255))),
|
||||
None,
|
||||
);
|
||||
|
||||
let pipeline_id = PipelineId(0, 0);
|
||||
let layout_size = LayoutSize::new(width as f32, height as f32);
|
||||
let mut builder = webrender_traits::DisplayListBuilder::new(pipeline_id, layout_size);
|
||||
|
||||
let bounds = LayoutRect::new(LayoutPoint::zero(), layout_size);
|
||||
builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
|
||||
bounds,
|
||||
None,
|
||||
TransformStyle::Flat,
|
||||
None,
|
||||
webrender_traits::MixBlendMode::Normal,
|
||||
Vec::new());
|
||||
|
||||
let clip = builder.push_clip_region(&bounds, vec![], None);
|
||||
builder.push_image(
|
||||
LayoutRect::new(LayoutPoint::new(30.0, 30.0), LayoutSize::new(500.0, 500.0)),
|
||||
clip,
|
||||
LayoutSize::new(500.0, 500.0),
|
||||
LayoutSize::new(0.0, 0.0),
|
||||
ImageRendering::Auto,
|
||||
blob_img1,
|
||||
);
|
||||
|
||||
let clip = builder.push_clip_region(&bounds, vec![], None);
|
||||
builder.push_image(
|
||||
LayoutRect::new(LayoutPoint::new(600.0, 60.0), LayoutSize::new(200.0, 200.0)),
|
||||
clip,
|
||||
LayoutSize::new(200.0, 200.0),
|
||||
LayoutSize::new(0.0, 0.0),
|
||||
ImageRendering::Auto,
|
||||
blob_img2,
|
||||
);
|
||||
|
||||
builder.pop_stacking_context();
|
||||
|
||||
api.set_display_list(
|
||||
Some(root_background_color),
|
||||
epoch,
|
||||
LayoutSize::new(width as f32, height as f32),
|
||||
builder.finalize(),
|
||||
true);
|
||||
api.set_root_pipeline(pipeline_id);
|
||||
api.generate_frame(None);
|
||||
|
||||
'outer: for event in window.wait_events() {
|
||||
let mut events = Vec::new();
|
||||
events.push(event);
|
||||
|
||||
for event in window.poll_events() {
|
||||
events.push(event);
|
||||
}
|
||||
|
||||
for event in events {
|
||||
match event {
|
||||
glutin::Event::Closed |
|
||||
glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
|
||||
glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Q)) => break 'outer,
|
||||
glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
|
||||
_, Some(glutin::VirtualKeyCode::P)) => {
|
||||
let enable_profiler = !renderer.get_profiler_enabled();
|
||||
renderer.set_profiler_enabled(enable_profiler);
|
||||
api.generate_frame(None);
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
renderer.update();
|
||||
renderer.render(DeviceUintSize::new(width, height));
|
||||
window.swap_buffers().ok();
|
||||
}
|
||||
}
|
||||
|
||||
struct Notifier {
|
||||
window_proxy: glutin::WindowProxy,
|
||||
}
|
||||
|
||||
impl Notifier {
|
||||
fn new(window_proxy: glutin::WindowProxy) -> Notifier {
|
||||
Notifier {
|
||||
window_proxy: window_proxy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl webrender_traits::RenderNotifier for Notifier {
|
||||
fn new_frame_ready(&mut self) {
|
||||
#[cfg(not(target_os = "android"))]
|
||||
self.window_proxy.wakeup_event_loop();
|
||||
}
|
||||
|
||||
fn new_scroll_frame_ready(&mut self, _composite_needed: bool) {
|
||||
#[cfg(not(target_os = "android"))]
|
||||
self.window_proxy.wakeup_event_loop();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,155 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use gleam::gl;
|
||||
use glutin;
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use webrender;
|
||||
use webrender_traits::*;
|
||||
|
||||
struct Notifier {
|
||||
window_proxy: glutin::WindowProxy,
|
||||
}
|
||||
|
||||
impl Notifier {
|
||||
fn new(window_proxy: glutin::WindowProxy) -> Notifier {
|
||||
Notifier {
|
||||
window_proxy: window_proxy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderNotifier for Notifier {
|
||||
fn new_frame_ready(&mut self) {
|
||||
#[cfg(not(target_os = "android"))]
|
||||
self.window_proxy.wakeup_event_loop();
|
||||
}
|
||||
|
||||
fn new_scroll_frame_ready(&mut self, _composite_needed: bool) {
|
||||
#[cfg(not(target_os = "android"))]
|
||||
self.window_proxy.wakeup_event_loop();
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HandyDandyRectBuilder {
|
||||
fn to(&self, x2: i32, y2: i32) -> LayoutRect;
|
||||
fn by(&self, w: i32, h: i32) -> LayoutRect;
|
||||
}
|
||||
// Allows doing `(x, y).to(x2, y2)` or `(x, y).by(width, height)` with i32
|
||||
// values to build a f32 LayoutRect
|
||||
impl HandyDandyRectBuilder for (i32, i32) {
|
||||
fn to(&self, x2: i32, y2: i32) -> LayoutRect {
|
||||
LayoutRect::new(LayoutPoint::new(self.0 as f32, self.1 as f32),
|
||||
LayoutSize::new((x2 - self.0) as f32, (y2 - self.1) as f32))
|
||||
}
|
||||
|
||||
fn by(&self, w: i32, h: i32) -> LayoutRect {
|
||||
LayoutRect::new(LayoutPoint::new(self.0 as f32, self.1 as f32),
|
||||
LayoutSize::new(w as f32, h as f32))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main_wrapper(builder_callback: fn(&RenderApi,
|
||||
&mut DisplayListBuilder,
|
||||
&PipelineId,
|
||||
&LayoutSize) -> (),
|
||||
event_handler: fn(&glutin::Event,
|
||||
&RenderApi) -> (),
|
||||
options: Option<webrender::RendererOptions>)
|
||||
{
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let res_path = if args.len() > 1 {
|
||||
Some(PathBuf::from(&args[1]))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let window = glutin::WindowBuilder::new()
|
||||
.with_title("WebRender Sample App")
|
||||
.with_multitouch()
|
||||
.with_gl(glutin::GlRequest::GlThenGles {
|
||||
opengl_version: (3, 2),
|
||||
opengles_version: (3, 0)
|
||||
})
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
unsafe {
|
||||
window.make_current().ok();
|
||||
}
|
||||
|
||||
let gl = match gl::GlType::default() {
|
||||
gl::GlType::Gl => unsafe { gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
|
||||
gl::GlType::Gles => unsafe { gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
|
||||
};
|
||||
|
||||
println!("OpenGL version {}", gl.get_string(gl::VERSION));
|
||||
println!("Shader resource path: {:?}", res_path);
|
||||
|
||||
let (width, height) = window.get_inner_size_pixels().unwrap();
|
||||
|
||||
let opts = webrender::RendererOptions {
|
||||
resource_override_path: res_path,
|
||||
debug: true,
|
||||
precache_shaders: true,
|
||||
device_pixel_ratio: window.hidpi_factor(),
|
||||
.. options.unwrap_or(webrender::RendererOptions::default())
|
||||
};
|
||||
|
||||
let size = DeviceUintSize::new(width, height);
|
||||
let (mut renderer, sender) = webrender::renderer::Renderer::new(gl, opts, size).unwrap();
|
||||
let api = sender.create_api();
|
||||
|
||||
let notifier = Box::new(Notifier::new(window.create_window_proxy()));
|
||||
renderer.set_render_notifier(notifier);
|
||||
|
||||
let epoch = Epoch(0);
|
||||
let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0);
|
||||
|
||||
let pipeline_id = PipelineId(0, 0);
|
||||
let layout_size = LayoutSize::new(width as f32, height as f32);
|
||||
let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
|
||||
|
||||
builder_callback(&api, &mut builder, &pipeline_id, &layout_size);
|
||||
|
||||
api.set_display_list(
|
||||
Some(root_background_color),
|
||||
epoch,
|
||||
LayoutSize::new(width as f32, height as f32),
|
||||
builder.finalize(),
|
||||
true);
|
||||
api.set_root_pipeline(pipeline_id);
|
||||
api.generate_frame(None);
|
||||
|
||||
'outer: for event in window.wait_events() {
|
||||
let mut events = Vec::new();
|
||||
events.push(event);
|
||||
|
||||
for event in window.poll_events() {
|
||||
events.push(event);
|
||||
}
|
||||
|
||||
for event in events {
|
||||
match event {
|
||||
glutin::Event::Closed |
|
||||
glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
|
||||
glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Q)) => break 'outer,
|
||||
|
||||
glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
|
||||
_, Some(glutin::VirtualKeyCode::P)) => {
|
||||
let enable_profiler = !renderer.get_profiler_enabled();
|
||||
renderer.set_profiler_enabled(enable_profiler);
|
||||
api.generate_frame(None);
|
||||
}
|
||||
|
||||
_ => event_handler(&event, &api),
|
||||
}
|
||||
}
|
||||
|
||||
renderer.update();
|
||||
renderer.render(DeviceUintSize::new(width, height));
|
||||
window.swap_buffers().ok();
|
||||
}
|
||||
}
|
|
@ -7,22 +7,103 @@ extern crate glutin;
|
|||
extern crate webrender;
|
||||
extern crate webrender_traits;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
use gleam::gl;
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use webrender_traits::{ClipId, ColorF, DeviceUintSize, Epoch, LayoutPoint, LayoutRect};
|
||||
use webrender_traits::{LayoutSize, PipelineId, ScrollEventPhase, ScrollLocation, TransformStyle};
|
||||
use webrender_traits::WorldPoint;
|
||||
|
||||
#[path="common/boilerplate.rs"]
|
||||
mod boilerplate;
|
||||
struct Notifier {
|
||||
window_proxy: glutin::WindowProxy,
|
||||
}
|
||||
|
||||
use boilerplate::HandyDandyRectBuilder;
|
||||
use std::sync::Mutex;
|
||||
use webrender_traits::*;
|
||||
impl Notifier {
|
||||
fn new(window_proxy: glutin::WindowProxy) -> Notifier {
|
||||
Notifier {
|
||||
window_proxy: window_proxy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn body(_api: &RenderApi,
|
||||
builder: &mut DisplayListBuilder,
|
||||
pipeline_id: &PipelineId,
|
||||
layout_size: &LayoutSize)
|
||||
{
|
||||
let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
|
||||
impl webrender_traits::RenderNotifier for Notifier {
|
||||
fn new_frame_ready(&mut self) {
|
||||
#[cfg(not(target_os = "android"))]
|
||||
self.window_proxy.wakeup_event_loop();
|
||||
}
|
||||
|
||||
fn new_scroll_frame_ready(&mut self, _composite_needed: bool) {
|
||||
#[cfg(not(target_os = "android"))]
|
||||
self.window_proxy.wakeup_event_loop();
|
||||
}
|
||||
}
|
||||
|
||||
trait HandyDandyRectBuilder {
|
||||
fn to(&self, x2: i32, y2: i32) -> LayoutRect;
|
||||
}
|
||||
// Allows doing `(x, y).to(x2, y2)` to build a LayoutRect
|
||||
impl HandyDandyRectBuilder for (i32, i32) {
|
||||
fn to(&self, x2: i32, y2: i32) -> LayoutRect {
|
||||
LayoutRect::new(LayoutPoint::new(self.0 as f32, self.1 as f32),
|
||||
LayoutSize::new((x2 - self.0) as f32, (y2 - self.1) as f32))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let res_path = if args.len() > 1 {
|
||||
Some(PathBuf::from(&args[1]))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let window = glutin::WindowBuilder::new()
|
||||
.with_title("WebRender Scrolling Sample")
|
||||
.with_gl(glutin::GlRequest::GlThenGles {
|
||||
opengl_version: (3, 2),
|
||||
opengles_version: (3, 0)
|
||||
})
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
unsafe {
|
||||
window.make_current().ok();
|
||||
}
|
||||
|
||||
let gl = match gl::GlType::default() {
|
||||
gl::GlType::Gl => unsafe { gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
|
||||
gl::GlType::Gles => unsafe { gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
|
||||
};
|
||||
|
||||
println!("OpenGL version {}", gl.get_string(gl::VERSION));
|
||||
println!("Shader resource path: {:?}", res_path);
|
||||
|
||||
let (width, height) = window.get_inner_size_pixels().unwrap();
|
||||
|
||||
let opts = webrender::RendererOptions {
|
||||
resource_override_path: res_path,
|
||||
debug: true,
|
||||
precache_shaders: true,
|
||||
device_pixel_ratio: window.hidpi_factor(),
|
||||
.. Default::default()
|
||||
};
|
||||
|
||||
let size = DeviceUintSize::new(width, height);
|
||||
let (mut renderer, sender) = webrender::renderer::Renderer::new(gl, opts, size).unwrap();
|
||||
let api = sender.create_api();
|
||||
|
||||
let notifier = Box::new(Notifier::new(window.create_window_proxy()));
|
||||
renderer.set_render_notifier(notifier);
|
||||
|
||||
let epoch = Epoch(0);
|
||||
let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0);
|
||||
|
||||
let pipeline_id = PipelineId(0, 0);
|
||||
let layout_size = LayoutSize::new(width as f32, height as f32);
|
||||
let mut builder = webrender_traits::DisplayListBuilder::new(pipeline_id, layout_size);
|
||||
|
||||
let bounds = LayoutRect::new(LayoutPoint::zero(), layout_size);
|
||||
builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
|
||||
bounds,
|
||||
None,
|
||||
|
@ -46,7 +127,7 @@ fn body(_api: &RenderApi,
|
|||
let clip = builder.push_clip_region(&scrollbox, vec![], None);
|
||||
let clip_id = builder.define_clip((0, 0).to(1000, 1000),
|
||||
clip,
|
||||
Some(ClipId::new(42, *pipeline_id)));
|
||||
Some(ClipId::new(42, pipeline_id)));
|
||||
builder.push_clip_id(clip_id);
|
||||
// now put some content into it.
|
||||
// start with a white background
|
||||
|
@ -72,7 +153,7 @@ fn body(_api: &RenderApi,
|
|||
let clip = builder.push_clip_region(&(0, 100).to(200, 300), vec![], None);
|
||||
let nested_clip_id = builder.define_clip((0, 100).to(300, 400),
|
||||
clip,
|
||||
Some(ClipId::new(43, *pipeline_id)));
|
||||
Some(ClipId::new(43, pipeline_id)));
|
||||
builder.push_clip_id(nested_clip_id);
|
||||
// give it a giant gray background just to distinguish it and to easily
|
||||
// visually identify the nested scrollbox
|
||||
|
@ -100,51 +181,68 @@ fn body(_api: &RenderApi,
|
|||
}
|
||||
|
||||
builder.pop_stacking_context();
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref CURSOR_POSITION: Mutex<WorldPoint> = Mutex::new(WorldPoint::zero());
|
||||
}
|
||||
api.set_display_list(
|
||||
Some(root_background_color),
|
||||
epoch,
|
||||
LayoutSize::new(width as f32, height as f32),
|
||||
builder.finalize(),
|
||||
true);
|
||||
api.set_root_pipeline(pipeline_id);
|
||||
api.generate_frame(None);
|
||||
|
||||
fn event_handler(event: &glutin::Event,
|
||||
api: &RenderApi)
|
||||
{
|
||||
match *event {
|
||||
glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
|
||||
let offset = match key {
|
||||
glutin::VirtualKeyCode::Down => (0.0, -10.0),
|
||||
glutin::VirtualKeyCode::Up => (0.0, 10.0),
|
||||
glutin::VirtualKeyCode::Right => (-10.0, 0.0),
|
||||
glutin::VirtualKeyCode::Left => (10.0, 0.0),
|
||||
_ => return,
|
||||
};
|
||||
let mut cursor_position = WorldPoint::zero();
|
||||
|
||||
api.scroll(ScrollLocation::Delta(LayoutVector2D::new(offset.0, offset.1)),
|
||||
*CURSOR_POSITION.lock().unwrap(),
|
||||
ScrollEventPhase::Start);
|
||||
'outer: for event in window.wait_events() {
|
||||
let mut events = Vec::new();
|
||||
events.push(event);
|
||||
|
||||
for event in window.poll_events() {
|
||||
events.push(event);
|
||||
}
|
||||
glutin::Event::MouseMoved(x, y) => {
|
||||
*CURSOR_POSITION.lock().unwrap() = WorldPoint::new(x as f32, y as f32);
|
||||
}
|
||||
glutin::Event::MouseWheel(delta, _, event_cursor_position) => {
|
||||
if let Some((x, y)) = event_cursor_position {
|
||||
*CURSOR_POSITION.lock().unwrap() = WorldPoint::new(x as f32, y as f32);
|
||||
|
||||
for event in events {
|
||||
match event {
|
||||
glutin::Event::Closed |
|
||||
glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
|
||||
glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Q)) => break 'outer,
|
||||
glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
|
||||
let offset = match key {
|
||||
glutin::VirtualKeyCode::Down => (0.0, -10.0),
|
||||
glutin::VirtualKeyCode::Up => (0.0, 10.0),
|
||||
glutin::VirtualKeyCode::Right => (-10.0, 0.0),
|
||||
glutin::VirtualKeyCode::Left => (10.0, 0.0),
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
api.scroll(ScrollLocation::Delta(LayoutPoint::new(offset.0, offset.1)),
|
||||
cursor_position,
|
||||
ScrollEventPhase::Start);
|
||||
}
|
||||
glutin::Event::MouseMoved(x, y) => {
|
||||
cursor_position = WorldPoint::new(x as f32, y as f32);
|
||||
}
|
||||
glutin::Event::MouseWheel(delta, _, event_cursor_position) => {
|
||||
if let Some((x, y)) = event_cursor_position {
|
||||
cursor_position = WorldPoint::new(x as f32, y as f32);
|
||||
}
|
||||
|
||||
const LINE_HEIGHT: f32 = 38.0;
|
||||
let (dx, dy) = match delta {
|
||||
glutin::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
|
||||
glutin::MouseScrollDelta::PixelDelta(dx, dy) => (dx, dy),
|
||||
};
|
||||
|
||||
api.scroll(ScrollLocation::Delta(LayoutPoint::new(dx, dy)),
|
||||
cursor_position,
|
||||
ScrollEventPhase::Start);
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
|
||||
const LINE_HEIGHT: f32 = 38.0;
|
||||
let (dx, dy) = match delta {
|
||||
glutin::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
|
||||
glutin::MouseScrollDelta::PixelDelta(dx, dy) => (dx, dy),
|
||||
};
|
||||
|
||||
api.scroll(ScrollLocation::Delta(LayoutVector2D::new(dx, dy)),
|
||||
*CURSOR_POSITION.lock().unwrap(),
|
||||
ScrollEventPhase::Start);
|
||||
}
|
||||
_ => ()
|
||||
|
||||
renderer.update();
|
||||
renderer.render(DeviceUintSize::new(width, height));
|
||||
window.swap_buffers().ok();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
boilerplate::main_wrapper(body, event_handler, None);
|
||||
}
|
||||
|
|
|
@ -8,19 +8,18 @@
|
|||
// as text-shadow.
|
||||
|
||||
void main(void) {
|
||||
Primitive prim = load_primitive();
|
||||
TextRun text = fetch_text_run(prim.specific_prim_address);
|
||||
|
||||
int glyph_index = prim.user_data0;
|
||||
int resource_address = prim.user_data1;
|
||||
Glyph glyph = fetch_glyph(prim.specific_prim_address, glyph_index);
|
||||
ResourceRect res = fetch_resource_rect(resource_address + glyph_index);
|
||||
PrimitiveInstance pi = fetch_prim_instance();
|
||||
RenderTaskData task = fetch_render_task(pi.render_task_index);
|
||||
TextRun text = fetch_text_run(pi.specific_prim_address);
|
||||
Glyph glyph = fetch_glyph(pi.user_data0);
|
||||
PrimitiveGeometry pg = fetch_prim_geometry(pi.global_prim_index);
|
||||
ResourceRect res = fetch_resource_rect(pi.user_data1);
|
||||
|
||||
// Glyphs size is already in device-pixels.
|
||||
// The render task origin is in device-pixels. Offset that by
|
||||
// the glyph offset, relative to its primitive bounding rect.
|
||||
vec2 size = res.uv_rect.zw - res.uv_rect.xy;
|
||||
vec2 origin = prim.task.screen_space_origin + uDevicePixelRatio * (glyph.offset - prim.local_rect.p0);
|
||||
vec2 origin = task.data0.xy + uDevicePixelRatio * (glyph.offset.xy - pg.local_rect.p0);
|
||||
vec4 local_rect = vec4(origin, size);
|
||||
|
||||
vec2 texture_size = vec2(textureSize(sColor0, 0));
|
||||
|
|
|
@ -106,43 +106,21 @@ varying vec3 vClipMaskUv;
|
|||
flat varying vec4 vLocalBounds;
|
||||
#endif
|
||||
|
||||
// TODO(gw): This is here temporarily while we have
|
||||
// both GPU store and cache. When the GPU
|
||||
// store code is removed, we can change the
|
||||
// PrimitiveInstance instance structure to
|
||||
// use 2x unsigned shorts as vertex attributes
|
||||
// instead of an int, and encode the UV directly
|
||||
// in the vertices.
|
||||
ivec2 get_resource_cache_uv(int address) {
|
||||
return ivec2(address % WR_MAX_VERTEX_TEXTURE_WIDTH,
|
||||
address / WR_MAX_VERTEX_TEXTURE_WIDTH);
|
||||
}
|
||||
|
||||
uniform sampler2D sResourceCache;
|
||||
|
||||
vec4[2] fetch_from_resource_cache_2(int address) {
|
||||
ivec2 uv = get_resource_cache_uv(address);
|
||||
return vec4[2](
|
||||
texelFetchOffset(sResourceCache, uv, 0, ivec2(0, 0)),
|
||||
texelFetchOffset(sResourceCache, uv, 0, ivec2(1, 0))
|
||||
);
|
||||
}
|
||||
|
||||
#ifdef WR_VERTEX_SHADER
|
||||
|
||||
#define VECS_PER_LAYER 9
|
||||
#define VECS_PER_RENDER_TASK 3
|
||||
#define VECS_PER_PRIM_HEADER 2
|
||||
#define VECS_PER_TEXT_RUN 1
|
||||
#define VECS_PER_GRADIENT 3
|
||||
#define VECS_PER_GRADIENT_STOP 2
|
||||
#define VECS_PER_PRIM_GEOM 2
|
||||
#define VECS_PER_SPLIT_GEOM 3
|
||||
|
||||
uniform sampler2D sLayers;
|
||||
uniform sampler2D sRenderTasks;
|
||||
uniform sampler2D sPrimGeometry;
|
||||
|
||||
uniform sampler2D sData16;
|
||||
uniform sampler2D sData32;
|
||||
uniform sampler2D sResourceRects;
|
||||
uniform sampler2D sResourceCache;
|
||||
|
||||
// Instanced attributes
|
||||
in ivec4 aData0;
|
||||
|
@ -167,6 +145,18 @@ vec4[2] fetch_data_2(int index) {
|
|||
);
|
||||
}
|
||||
|
||||
// TODO(gw): This is here temporarily while we have
|
||||
// both GPU store and cache. When the GPU
|
||||
// store code is removed, we can change the
|
||||
// PrimitiveInstance instance structure to
|
||||
// use 2x unsigned shorts as vertex attributes
|
||||
// instead of an int, and encode the UV directly
|
||||
// in the vertices.
|
||||
ivec2 get_resource_cache_uv(int address) {
|
||||
return ivec2(address % WR_MAX_VERTEX_TEXTURE_WIDTH,
|
||||
address / WR_MAX_VERTEX_TEXTURE_WIDTH);
|
||||
}
|
||||
|
||||
vec4[8] fetch_from_resource_cache_8(int address) {
|
||||
ivec2 uv = get_resource_cache_uv(address);
|
||||
return vec4[8](
|
||||
|
@ -331,8 +321,8 @@ struct GradientStop {
|
|||
vec4 offset;
|
||||
};
|
||||
|
||||
GradientStop fetch_gradient_stop(int address) {
|
||||
vec4 data[2] = fetch_from_resource_cache_2(address);
|
||||
GradientStop fetch_gradient_stop(int index) {
|
||||
vec4 data[2] = fetch_data_2(index);
|
||||
return GradientStop(data[0], data[1]);
|
||||
}
|
||||
|
||||
|
@ -427,18 +417,12 @@ BorderCorners get_border_corners(Border border, RectWithSize local_rect) {
|
|||
}
|
||||
|
||||
struct Glyph {
|
||||
vec2 offset;
|
||||
vec4 offset;
|
||||
};
|
||||
|
||||
Glyph fetch_glyph(int specific_prim_address, int glyph_index) {
|
||||
// Two glyphs are packed in each texel in the GPU cache.
|
||||
int glyph_address = specific_prim_address +
|
||||
VECS_PER_TEXT_RUN +
|
||||
glyph_index / 2;
|
||||
vec4 data = fetch_from_resource_cache_1(glyph_address);
|
||||
// Select XY or ZW based on glyph index.
|
||||
vec2 glyph = mix(data.xy, data.zw, bvec2(glyph_index % 2 == 1));
|
||||
return Glyph(glyph);
|
||||
Glyph fetch_glyph(int index) {
|
||||
vec4 data = fetch_data_1(index);
|
||||
return Glyph(data);
|
||||
}
|
||||
|
||||
RectWithSize fetch_instance_geometry(int address) {
|
||||
|
@ -446,8 +430,26 @@ RectWithSize fetch_instance_geometry(int address) {
|
|||
return RectWithSize(data.xy, data.zw);
|
||||
}
|
||||
|
||||
struct PrimitiveGeometry {
|
||||
RectWithSize local_rect;
|
||||
RectWithSize local_clip_rect;
|
||||
};
|
||||
|
||||
PrimitiveGeometry fetch_prim_geometry(int index) {
|
||||
PrimitiveGeometry pg;
|
||||
|
||||
ivec2 uv = get_fetch_uv(index, VECS_PER_PRIM_GEOM);
|
||||
|
||||
vec4 local_rect = texelFetchOffset(sPrimGeometry, uv, 0, ivec2(0, 0));
|
||||
pg.local_rect = RectWithSize(local_rect.xy, local_rect.zw);
|
||||
vec4 local_clip_rect = texelFetchOffset(sPrimGeometry, uv, 0, ivec2(1, 0));
|
||||
pg.local_clip_rect = RectWithSize(local_clip_rect.xy, local_clip_rect.zw);
|
||||
|
||||
return pg;
|
||||
}
|
||||
|
||||
struct PrimitiveInstance {
|
||||
int prim_address;
|
||||
int global_prim_index;
|
||||
int specific_prim_address;
|
||||
int render_task_index;
|
||||
int clip_task_index;
|
||||
|
@ -460,14 +462,14 @@ struct PrimitiveInstance {
|
|||
PrimitiveInstance fetch_prim_instance() {
|
||||
PrimitiveInstance pi;
|
||||
|
||||
pi.prim_address = aData0.x;
|
||||
pi.specific_prim_address = pi.prim_address + VECS_PER_PRIM_HEADER;
|
||||
pi.render_task_index = aData0.y;
|
||||
pi.clip_task_index = aData0.z;
|
||||
pi.layer_index = aData0.w;
|
||||
pi.z = aData1.x;
|
||||
pi.user_data0 = aData1.y;
|
||||
pi.user_data1 = aData1.z;
|
||||
pi.global_prim_index = aData0.x;
|
||||
pi.specific_prim_address = aData0.y;
|
||||
pi.render_task_index = aData0.z;
|
||||
pi.clip_task_index = aData0.w;
|
||||
pi.layer_index = aData1.x;
|
||||
pi.z = aData1.y;
|
||||
pi.user_data0 = aData1.z;
|
||||
pi.user_data1 = aData1.w;
|
||||
|
||||
return pi;
|
||||
}
|
||||
|
@ -501,26 +503,24 @@ struct Primitive {
|
|||
AlphaBatchTask task;
|
||||
RectWithSize local_rect;
|
||||
RectWithSize local_clip_rect;
|
||||
int specific_prim_address;
|
||||
int prim_index;
|
||||
int user_data0;
|
||||
int user_data1;
|
||||
float z;
|
||||
};
|
||||
|
||||
Primitive load_primitive() {
|
||||
PrimitiveInstance pi = fetch_prim_instance();
|
||||
|
||||
Primitive load_primitive_custom(PrimitiveInstance pi) {
|
||||
Primitive prim;
|
||||
|
||||
prim.layer = fetch_layer(pi.layer_index);
|
||||
prim.clip_area = fetch_clip_area(pi.clip_task_index);
|
||||
prim.task = fetch_alpha_batch_task(pi.render_task_index);
|
||||
|
||||
vec4 geom[2] = fetch_from_resource_cache_2(pi.prim_address);
|
||||
prim.local_rect = RectWithSize(geom[0].xy, geom[0].zw);
|
||||
prim.local_clip_rect = RectWithSize(geom[1].xy, geom[1].zw);
|
||||
PrimitiveGeometry pg = fetch_prim_geometry(pi.global_prim_index);
|
||||
prim.local_rect = pg.local_rect;
|
||||
prim.local_clip_rect = pg.local_clip_rect;
|
||||
|
||||
prim.specific_prim_address = pi.specific_prim_address;
|
||||
prim.prim_index = pi.specific_prim_address;
|
||||
prim.user_data0 = pi.user_data0;
|
||||
prim.user_data1 = pi.user_data1;
|
||||
prim.z = float(pi.z);
|
||||
|
@ -528,6 +528,13 @@ Primitive load_primitive() {
|
|||
return prim;
|
||||
}
|
||||
|
||||
Primitive load_primitive() {
|
||||
PrimitiveInstance pi = fetch_prim_instance();
|
||||
|
||||
return load_primitive_custom(pi);
|
||||
}
|
||||
|
||||
|
||||
// Return the intersection of the plane (set up by "normal" and "point")
|
||||
// with the ray (set up by "ray_origin" and "ray_dir"),
|
||||
// writing the resulting scaler into "t".
|
||||
|
@ -850,8 +857,10 @@ vec4 dither(vec4 color) {
|
|||
}
|
||||
#endif //WR_FEATURE_DITHERING
|
||||
|
||||
vec4 sample_gradient(int address, float offset, float gradient_repeat) {
|
||||
// Modulo the offset if the gradient repeats.
|
||||
vec4 sample_gradient(float offset, float gradient_repeat, float gradient_index, vec2 gradient_size) {
|
||||
// Modulo the offset if the gradient repeats. We don't need to clamp non-repeating
|
||||
// gradients because the gradient data texture is bound with CLAMP_TO_EDGE, and the
|
||||
// first and last color entries are filled with the first and last stop colors
|
||||
float x = mix(offset, fract(offset), gradient_repeat);
|
||||
|
||||
// Calculate the color entry index to use for this offset:
|
||||
|
@ -859,25 +868,22 @@ vec4 sample_gradient(int address, float offset, float gradient_repeat) {
|
|||
// offsets from [0, 1) use the color entries in the range of [1, N-1)
|
||||
// offsets >= 1 use the last color entry, N-1
|
||||
// so transform the range [0, 1) -> [1, N-1)
|
||||
|
||||
// TODO(gw): In the future we might consider making the size of the
|
||||
// LUT vary based on number / distribution of stops in the gradient.
|
||||
const int GRADIENT_ENTRIES = 128;
|
||||
x = 1.0 + x * float(GRADIENT_ENTRIES);
|
||||
float gradient_entries = 0.5 * gradient_size.x;
|
||||
x = x * (gradient_entries - 2.0) + 1.0;
|
||||
|
||||
// Calculate the texel to index into the gradient color entries:
|
||||
// floor(x) is the gradient color entry index
|
||||
// fract(x) is the linear filtering factor between start and end
|
||||
int lut_offset = 2 * int(floor(x)); // There is a [start, end] color per entry.
|
||||
// so, 2 * floor(x) + 0.5 is the center of the start color
|
||||
// finally, add floor(x) to interpolate to end
|
||||
x = 2.0 * floor(x) + 0.5 + fract(x);
|
||||
|
||||
// Ensure we don't fetch outside the valid range of the LUT.
|
||||
lut_offset = clamp(lut_offset, 0, 2 * (GRADIENT_ENTRIES + 1));
|
||||
// Gradient color entries are encoded with high bits in one row and low bits in the next
|
||||
// So use linear filtering to mix (gradient_index + 1) with (gradient_index)
|
||||
float y = gradient_index * 2.0 + 0.5 + 1.0 / 256.0;
|
||||
|
||||
// Fetch the start and end color.
|
||||
vec4 texels[2] = fetch_from_resource_cache_2(address + lut_offset);
|
||||
|
||||
// Finally interpolate and apply dithering
|
||||
return dither(mix(texels[0], texels[1], fract(x)));
|
||||
// Finally sample and apply dithering
|
||||
return dither(texture(sGradients, vec2(x, y) / gradient_size));
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#line 1
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
@ -14,7 +12,8 @@ void main(void) {
|
|||
|
||||
float offset = dot(pos - vStartPoint, vScaledDir);
|
||||
|
||||
oFragColor = sample_gradient(vGradientAddress,
|
||||
offset,
|
||||
vGradientRepeat);
|
||||
oFragColor = sample_gradient(offset,
|
||||
vGradientRepeat,
|
||||
vGradientIndex,
|
||||
vGradientTextureSize);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
flat varying int vGradientAddress;
|
||||
flat varying float vGradientIndex;
|
||||
flat varying vec2 vGradientTextureSize;
|
||||
flat varying float vGradientRepeat;
|
||||
|
||||
flat varying vec2 vScaledDir;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
void main(void) {
|
||||
Primitive prim = load_primitive();
|
||||
Gradient gradient = fetch_gradient(prim.specific_prim_address);
|
||||
Gradient gradient = fetch_gradient(prim.prim_index);
|
||||
|
||||
VertexInfo vi = write_vertex(prim.local_rect,
|
||||
prim.local_clip_rect,
|
||||
|
@ -26,7 +26,11 @@ void main(void) {
|
|||
vTileSize = gradient.tile_size_repeat.xy;
|
||||
vTileRepeat = gradient.tile_size_repeat.zw;
|
||||
|
||||
vGradientAddress = prim.specific_prim_address + VECS_PER_GRADIENT;
|
||||
// V coordinate of gradient row in lookup texture.
|
||||
vGradientIndex = float(prim.user_data0);
|
||||
|
||||
// The texture size of the lookup texture
|
||||
vGradientTextureSize = vec2(textureSize(sGradients, 0));
|
||||
|
||||
// Whether to repeat the gradient instead of clamping.
|
||||
vGradientRepeat = float(int(gradient.extend_mode.x) == EXTEND_MODE_REPEAT);
|
||||
|
|
|
@ -117,7 +117,7 @@ int select_style(int color_select, vec2 fstyle) {
|
|||
|
||||
void main(void) {
|
||||
Primitive prim = load_primitive();
|
||||
Border border = fetch_border(prim.specific_prim_address);
|
||||
Border border = fetch_border(prim.prim_index);
|
||||
int sub_part = prim.user_data0;
|
||||
BorderCorners corners = get_border_corners(border, prim.local_rect);
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@ void write_clip_params(float style,
|
|||
|
||||
void main(void) {
|
||||
Primitive prim = load_primitive();
|
||||
Border border = fetch_border(prim.specific_prim_address);
|
||||
Border border = fetch_border(prim.prim_index);
|
||||
int sub_part = prim.user_data0;
|
||||
BorderCorners corners = get_border_corners(border, prim.local_rect);
|
||||
vec4 color = border.colors[sub_part];
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
void main(void) {
|
||||
Primitive prim = load_primitive();
|
||||
BoxShadow bs = fetch_boxshadow(prim.specific_prim_address);
|
||||
RectWithSize segment_rect = fetch_instance_geometry(prim.specific_prim_address + BS_HEADER_VECS + prim.user_data0);
|
||||
BoxShadow bs = fetch_boxshadow(prim.prim_index);
|
||||
RectWithSize segment_rect = fetch_instance_geometry(prim.prim_index + BS_HEADER_VECS + prim.user_data0);
|
||||
|
||||
VertexInfo vi = write_vertex(segment_rect,
|
||||
prim.local_clip_rect,
|
||||
|
|
|
@ -5,16 +5,12 @@
|
|||
|
||||
void main(void) {
|
||||
Primitive prim = load_primitive();
|
||||
Gradient gradient = fetch_gradient(prim.specific_prim_address);
|
||||
Gradient gradient = fetch_gradient(prim.prim_index);
|
||||
|
||||
vec4 abs_start_end_point = gradient.start_end_point + prim.local_rect.p0.xyxy;
|
||||
|
||||
int stop_address = prim.specific_prim_address +
|
||||
VECS_PER_GRADIENT +
|
||||
VECS_PER_GRADIENT_STOP * prim.user_data0;
|
||||
|
||||
GradientStop g0 = fetch_gradient_stop(stop_address);
|
||||
GradientStop g1 = fetch_gradient_stop(stop_address + VECS_PER_GRADIENT_STOP);
|
||||
GradientStop g0 = fetch_gradient_stop(prim.user_data0 + 0);
|
||||
GradientStop g1 = fetch_gradient_stop(prim.user_data0 + 1);
|
||||
|
||||
RectWithSize segment_rect;
|
||||
vec2 axis;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
void main(void) {
|
||||
Primitive prim = load_primitive();
|
||||
Image image = fetch_image(prim.specific_prim_address);
|
||||
Image image = fetch_image(prim.prim_index);
|
||||
ResourceRect res = fetch_resource_rect(prim.user_data0);
|
||||
|
||||
#ifdef WR_FEATURE_TRANSFORM
|
||||
|
|
|
@ -49,7 +49,8 @@ void main(void) {
|
|||
}
|
||||
}
|
||||
|
||||
oFragColor = sample_gradient(vGradientAddress,
|
||||
offset,
|
||||
vGradientRepeat);
|
||||
oFragColor = sample_gradient(offset,
|
||||
vGradientRepeat,
|
||||
vGradientIndex,
|
||||
vGradientTextureSize);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
flat varying int vGradientAddress;
|
||||
flat varying float vGradientIndex;
|
||||
flat varying vec2 vGradientTextureSize;
|
||||
flat varying float vGradientRepeat;
|
||||
|
||||
flat varying vec2 vStartCenter;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
void main(void) {
|
||||
Primitive prim = load_primitive();
|
||||
RadialGradient gradient = fetch_radial_gradient(prim.specific_prim_address);
|
||||
RadialGradient gradient = fetch_radial_gradient(prim.prim_index);
|
||||
|
||||
VertexInfo vi = write_vertex(prim.local_rect,
|
||||
prim.local_clip_rect,
|
||||
|
@ -34,7 +34,11 @@ void main(void) {
|
|||
vTileSize.y *= ratio_xy;
|
||||
vTileRepeat.y *= ratio_xy;
|
||||
|
||||
vGradientAddress = prim.specific_prim_address + VECS_PER_GRADIENT;
|
||||
// V coordinate of gradient row in lookup texture.
|
||||
vGradientIndex = float(prim.user_data0);
|
||||
|
||||
// The texture size of the lookup texture
|
||||
vGradientTextureSize = vec2(textureSize(sGradients, 0));
|
||||
|
||||
// Whether to repeat the gradient instead of clamping.
|
||||
vGradientRepeat = float(int(gradient.start_end_radius_ratio_xy_extend_mode.w) == EXTEND_MODE_REPEAT);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
void main(void) {
|
||||
Primitive prim = load_primitive();
|
||||
Rectangle rect = fetch_rectangle(prim.specific_prim_address);
|
||||
Rectangle rect = fetch_rectangle(prim.prim_index);
|
||||
vColor = rect.color;
|
||||
#ifdef WR_FEATURE_TRANSFORM
|
||||
TransformVertexInfo vi = write_transform_vertex(prim.local_rect,
|
||||
|
|
|
@ -3,16 +3,18 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
uniform sampler2D sSplitGeometry;
|
||||
|
||||
struct SplitGeometry {
|
||||
vec3 points[4];
|
||||
};
|
||||
|
||||
SplitGeometry fetch_split_geometry(int address) {
|
||||
ivec2 uv = get_resource_cache_uv(address);
|
||||
SplitGeometry fetch_split_geometry(int index) {
|
||||
ivec2 uv = get_fetch_uv(index, VECS_PER_SPLIT_GEOM);
|
||||
|
||||
vec4 data0 = texelFetchOffset(sResourceCache, uv, 0, ivec2(0, 0));
|
||||
vec4 data1 = texelFetchOffset(sResourceCache, uv, 0, ivec2(1, 0));
|
||||
vec4 data2 = texelFetchOffset(sResourceCache, uv, 0, ivec2(2, 0));
|
||||
vec4 data0 = texelFetchOffset(sSplitGeometry, uv, 0, ivec2(0, 0));
|
||||
vec4 data1 = texelFetchOffset(sSplitGeometry, uv, 0, ivec2(1, 0));
|
||||
vec4 data2 = texelFetchOffset(sSplitGeometry, uv, 0, ivec2(2, 0));
|
||||
|
||||
SplitGeometry geo;
|
||||
geo.points = vec3[4](
|
||||
|
|
|
@ -5,14 +5,11 @@
|
|||
|
||||
void main(void) {
|
||||
Primitive prim = load_primitive();
|
||||
TextRun text = fetch_text_run(prim.specific_prim_address);
|
||||
TextRun text = fetch_text_run(prim.prim_index);
|
||||
Glyph glyph = fetch_glyph(prim.user_data0);
|
||||
ResourceRect res = fetch_resource_rect(prim.user_data1);
|
||||
|
||||
int glyph_index = prim.user_data0;
|
||||
int resource_address = prim.user_data1;
|
||||
Glyph glyph = fetch_glyph(prim.specific_prim_address, glyph_index);
|
||||
ResourceRect res = fetch_resource_rect(resource_address + glyph_index);
|
||||
|
||||
RectWithSize local_rect = RectWithSize(glyph.offset,
|
||||
RectWithSize local_rect = RectWithSize(glyph.offset.xy,
|
||||
(res.uv_rect.zw - res.uv_rect.xy) / uDevicePixelRatio);
|
||||
|
||||
#ifdef WR_FEATURE_TRANSFORM
|
||||
|
|
|
@ -67,7 +67,7 @@ void main(void) {
|
|||
#endif
|
||||
#endif
|
||||
|
||||
YuvImage image = fetch_yuv_image(prim.specific_prim_address);
|
||||
YuvImage image = fetch_yuv_image(prim.prim_index);
|
||||
vStretchSize = image.size;
|
||||
|
||||
vHalfTexelY = vec2(0.5) / y_texture_size_normalization_factor;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use euclid::Point3D;
|
||||
use geometry::ray_intersects_rect;
|
||||
use mask_cache::{ClipSource, MaskCacheInfo, RegionMode};
|
||||
use prim_store::GpuBlock32;
|
||||
|
@ -12,8 +13,7 @@ use util::TransformedRectKind;
|
|||
use webrender_traits::{ClipId, ClipRegion, DeviceIntRect, LayerPixel, LayerPoint, LayerRect};
|
||||
use webrender_traits::{LayerSize, LayerToScrollTransform, LayerToWorldTransform, PipelineId};
|
||||
use webrender_traits::{ScrollClamping, ScrollEventPhase, ScrollLayerRect, ScrollLocation};
|
||||
use webrender_traits::{WorldPoint, LayerVector2D};
|
||||
use webrender_traits::{as_scroll_parent_vector};
|
||||
use webrender_traits::{WorldPoint, WorldPoint4D};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
const CAN_OVERSCROLL: bool = true;
|
||||
|
@ -72,14 +72,14 @@ pub enum NodeType {
|
|||
|
||||
/// Other nodes just do clipping, but no transformation.
|
||||
Clip(ClipInfo),
|
||||
|
||||
/// Other nodes just do clipping, but no transformation.
|
||||
ScrollFrame(ScrollingState),
|
||||
}
|
||||
|
||||
/// Contains information common among all types of ClipScrollTree nodes.
|
||||
/// Contains scrolling and transform information stacking contexts.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ClipScrollNode {
|
||||
/// Manages scrolling offset, overscroll state etc.
|
||||
pub scrolling: ScrollingState,
|
||||
|
||||
/// Size of the content inside the scroll region (in logical pixels)
|
||||
pub content_size: LayerSize,
|
||||
|
||||
|
@ -105,7 +105,7 @@ pub struct ClipScrollNode {
|
|||
/// The scroll offset of all the nodes between us and our parent reference frame.
|
||||
/// This is used to calculate intersections between us and content or nodes that
|
||||
/// are also direct children of our reference frame.
|
||||
pub reference_frame_relative_scroll_offset: LayerVector2D,
|
||||
pub reference_frame_relative_scroll_offset: LayerPoint,
|
||||
|
||||
/// Pipeline that this layer belongs to
|
||||
pub pipeline_id: PipelineId,
|
||||
|
@ -121,26 +121,6 @@ pub struct ClipScrollNode {
|
|||
}
|
||||
|
||||
impl ClipScrollNode {
|
||||
pub fn new_scroll_frame(pipeline_id: PipelineId,
|
||||
parent_id: ClipId,
|
||||
content_rect: &LayerRect,
|
||||
frame_rect: &LayerRect)
|
||||
-> ClipScrollNode {
|
||||
ClipScrollNode {
|
||||
content_size: content_rect.size,
|
||||
local_viewport_rect: *frame_rect,
|
||||
local_clip_rect: *frame_rect,
|
||||
combined_local_viewport_rect: LayerRect::zero(),
|
||||
world_viewport_transform: LayerToWorldTransform::identity(),
|
||||
world_content_transform: LayerToWorldTransform::identity(),
|
||||
reference_frame_relative_scroll_offset: LayerVector2D::zero(),
|
||||
parent: Some(parent_id),
|
||||
children: Vec::new(),
|
||||
pipeline_id: pipeline_id,
|
||||
node_type: NodeType::ScrollFrame(ScrollingState::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(pipeline_id: PipelineId,
|
||||
parent_id: ClipId,
|
||||
content_rect: &LayerRect,
|
||||
|
@ -151,13 +131,14 @@ impl ClipScrollNode {
|
|||
// of the node.
|
||||
let local_viewport_rect = LayerRect::new(content_rect.origin, clip_rect.size);
|
||||
ClipScrollNode {
|
||||
scrolling: ScrollingState::new(),
|
||||
content_size: content_rect.size,
|
||||
local_viewport_rect: local_viewport_rect,
|
||||
local_clip_rect: local_viewport_rect,
|
||||
combined_local_viewport_rect: LayerRect::zero(),
|
||||
world_viewport_transform: LayerToWorldTransform::identity(),
|
||||
world_content_transform: LayerToWorldTransform::identity(),
|
||||
reference_frame_relative_scroll_offset: LayerVector2D::zero(),
|
||||
reference_frame_relative_scroll_offset: LayerPoint::zero(),
|
||||
parent: Some(parent_id),
|
||||
children: Vec::new(),
|
||||
pipeline_id: pipeline_id,
|
||||
|
@ -172,13 +153,14 @@ impl ClipScrollNode {
|
|||
pipeline_id: PipelineId)
|
||||
-> ClipScrollNode {
|
||||
ClipScrollNode {
|
||||
scrolling: ScrollingState::new(),
|
||||
content_size: content_size,
|
||||
local_viewport_rect: *local_viewport_rect,
|
||||
local_clip_rect: *local_viewport_rect,
|
||||
combined_local_viewport_rect: LayerRect::zero(),
|
||||
world_viewport_transform: LayerToWorldTransform::identity(),
|
||||
world_content_transform: LayerToWorldTransform::identity(),
|
||||
reference_frame_relative_scroll_offset: LayerVector2D::zero(),
|
||||
reference_frame_relative_scroll_offset: LayerPoint::zero(),
|
||||
parent: parent_id,
|
||||
children: Vec::new(),
|
||||
pipeline_id: pipeline_id,
|
||||
|
@ -190,26 +172,45 @@ impl ClipScrollNode {
|
|||
self.children.push(child);
|
||||
}
|
||||
|
||||
pub fn finalize(&mut self, new_scrolling: &ScrollingState) {
|
||||
match self.node_type {
|
||||
NodeType::ReferenceFrame(_) | NodeType::Clip(_) =>
|
||||
warn!("Tried to scroll a non-scroll node."),
|
||||
NodeType::ScrollFrame(ref mut scrolling) => *scrolling = *new_scrolling,
|
||||
}
|
||||
pub fn finalize(&mut self, scrolling: &ScrollingState) {
|
||||
self.scrolling = *scrolling;
|
||||
}
|
||||
|
||||
pub fn overscroll_amount(&self) -> LayerSize {
|
||||
let scrollable_width = self.scrollable_width();
|
||||
let overscroll_x = if self.scrolling.offset.x > 0.0 {
|
||||
-self.scrolling.offset.x
|
||||
} else if self.scrolling.offset.x < -scrollable_width {
|
||||
-scrollable_width - self.scrolling.offset.x
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let scrollable_height = self.scrollable_height();
|
||||
let overscroll_y = if self.scrolling.offset.y > 0.0 {
|
||||
-self.scrolling.offset.y
|
||||
} else if self.scrolling.offset.y < -scrollable_height {
|
||||
-scrollable_height - self.scrolling.offset.y
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
LayerSize::new(overscroll_x, overscroll_y)
|
||||
}
|
||||
|
||||
pub fn set_scroll_origin(&mut self, origin: &LayerPoint, clamp: ScrollClamping) -> bool {
|
||||
let scrollable_height = self.scrollable_height();
|
||||
let scrollable_width = self.scrollable_width();
|
||||
|
||||
let scrolling = match self.node_type {
|
||||
NodeType::ReferenceFrame(_) | NodeType::Clip(_) => {
|
||||
warn!("Tried to scroll a non-scroll node.");
|
||||
match self.node_type {
|
||||
NodeType::ReferenceFrame(_) => {
|
||||
warn!("Tried to scroll a reference frame.");
|
||||
return false;
|
||||
}
|
||||
NodeType::ScrollFrame(ref mut scrolling) => scrolling,
|
||||
NodeType::Clip(_) => {}
|
||||
};
|
||||
|
||||
|
||||
let scrollable_height = self.scrollable_height();
|
||||
let scrollable_width = self.scrollable_width();
|
||||
|
||||
let new_offset = match clamp {
|
||||
ScrollClamping::ToContentBounds => {
|
||||
if scrollable_height <= 0. && scrollable_width <= 0. {
|
||||
|
@ -217,35 +218,35 @@ impl ClipScrollNode {
|
|||
}
|
||||
|
||||
let origin = LayerPoint::new(origin.x.max(0.0), origin.y.max(0.0));
|
||||
LayerVector2D::new((-origin.x).max(-scrollable_width).min(0.0).round(),
|
||||
(-origin.y).max(-scrollable_height).min(0.0).round())
|
||||
LayerPoint::new((-origin.x).max(-scrollable_width).min(0.0).round(),
|
||||
(-origin.y).max(-scrollable_height).min(0.0).round())
|
||||
}
|
||||
ScrollClamping::NoClamping => LayerPoint::zero() - *origin,
|
||||
};
|
||||
|
||||
if new_offset == scrolling.offset {
|
||||
if new_offset == self.scrolling.offset {
|
||||
return false;
|
||||
}
|
||||
|
||||
scrolling.offset = new_offset;
|
||||
scrolling.bouncing_back = false;
|
||||
scrolling.started_bouncing_back = false;
|
||||
self.scrolling.offset = new_offset;
|
||||
self.scrolling.bouncing_back = false;
|
||||
self.scrolling.started_bouncing_back = false;
|
||||
true
|
||||
}
|
||||
|
||||
pub fn update_transform(&mut self,
|
||||
parent_reference_frame_transform: &LayerToWorldTransform,
|
||||
parent_combined_viewport_rect: &ScrollLayerRect,
|
||||
parent_scroll_offset: LayerVector2D,
|
||||
parent_accumulated_scroll_offset: LayerVector2D) {
|
||||
parent_scroll_offset: LayerPoint,
|
||||
parent_accumulated_scroll_offset: LayerPoint) {
|
||||
self.reference_frame_relative_scroll_offset = match self.node_type {
|
||||
NodeType::ReferenceFrame(_) => LayerVector2D::zero(),
|
||||
NodeType::Clip(_) | NodeType::ScrollFrame(..) => parent_accumulated_scroll_offset,
|
||||
NodeType::ReferenceFrame(_) => LayerPoint::zero(),
|
||||
NodeType::Clip(_) => parent_accumulated_scroll_offset,
|
||||
};
|
||||
|
||||
let local_transform = match self.node_type {
|
||||
NodeType::ReferenceFrame(transform) => transform,
|
||||
NodeType::Clip(_) | NodeType::ScrollFrame(..) => LayerToScrollTransform::identity(),
|
||||
NodeType::Clip(_) => LayerToScrollTransform::identity(),
|
||||
};
|
||||
|
||||
let inv_transform = match local_transform.inverse() {
|
||||
|
@ -264,14 +265,14 @@ impl ClipScrollNode {
|
|||
// local viewport rect doesn't include scrolling offsets so the only one that matters
|
||||
// is the relative offset between us and the parent.
|
||||
let parent_combined_viewport_in_local_space =
|
||||
inv_transform.pre_translate(-as_scroll_parent_vector(&parent_scroll_offset).to_3d())
|
||||
inv_transform.pre_translated(-parent_scroll_offset.x, -parent_scroll_offset.y, 0.0)
|
||||
.transform_rect(parent_combined_viewport_rect);
|
||||
|
||||
// Now that we have the combined viewport rectangle of the parent nodes in local space,
|
||||
// we do the intersection and get our combined viewport rect in the coordinate system
|
||||
// starting from our origin.
|
||||
self.combined_local_viewport_rect = match self.node_type {
|
||||
NodeType::Clip(_) | NodeType::ScrollFrame(..) => {
|
||||
NodeType::Clip(_) => {
|
||||
parent_combined_viewport_in_local_space.intersection(&self.local_clip_rect)
|
||||
.unwrap_or(LayerRect::zero())
|
||||
}
|
||||
|
@ -290,14 +291,17 @@ impl ClipScrollNode {
|
|||
// with the local_viewport_rect to get its position in world space.
|
||||
self.world_viewport_transform =
|
||||
parent_reference_frame_transform
|
||||
.pre_translate(parent_accumulated_scroll_offset.to_3d())
|
||||
.pre_translated(parent_accumulated_scroll_offset.x,
|
||||
parent_accumulated_scroll_offset.y,
|
||||
0.0)
|
||||
.pre_mul(&local_transform.with_destination::<LayerPixel>());
|
||||
|
||||
// The transformation for any content inside of us is the viewport transformation, plus
|
||||
// whatever scrolling offset we supply as well.
|
||||
let scroll_offset = self.scroll_offset();
|
||||
self.world_content_transform =
|
||||
self.world_viewport_transform.pre_translate(scroll_offset.to_3d());
|
||||
self.world_viewport_transform.pre_translated(self.scrolling.offset.x,
|
||||
self.scrolling.offset.y,
|
||||
0.0);
|
||||
}
|
||||
|
||||
pub fn scrollable_height(&self) -> f32 {
|
||||
|
@ -309,89 +313,95 @@ impl ClipScrollNode {
|
|||
}
|
||||
|
||||
pub fn scroll(&mut self, scroll_location: ScrollLocation, phase: ScrollEventPhase) -> bool {
|
||||
let scrollable_width = self.scrollable_width();
|
||||
let scrollable_height = self.scrollable_height();
|
||||
|
||||
let scrolling = match self.node_type {
|
||||
NodeType::ReferenceFrame(_) | NodeType::Clip(_) => return false,
|
||||
NodeType::ScrollFrame(ref mut scrolling) => scrolling,
|
||||
};
|
||||
|
||||
if scrolling.started_bouncing_back && phase == ScrollEventPhase::Move(false) {
|
||||
if self.scrolling.started_bouncing_back && phase == ScrollEventPhase::Move(false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut delta = match scroll_location {
|
||||
ScrollLocation::Delta(delta) => delta,
|
||||
ScrollLocation::Start => {
|
||||
if scrolling.offset.y.round() >= 0.0 {
|
||||
if self.scrolling.offset.y.round() >= 0.0 {
|
||||
// Nothing to do on this layer.
|
||||
return false;
|
||||
}
|
||||
|
||||
scrolling.offset.y = 0.0;
|
||||
self.scrolling.offset.y = 0.0;
|
||||
return true;
|
||||
},
|
||||
ScrollLocation::End => {
|
||||
let end_pos = self.local_viewport_rect.size.height - self.content_size.height;
|
||||
|
||||
if scrolling.offset.y.round() <= end_pos {
|
||||
if self.scrolling.offset.y.round() <= end_pos {
|
||||
// Nothing to do on this layer.
|
||||
return false;
|
||||
}
|
||||
|
||||
scrolling.offset.y = end_pos;
|
||||
self.scrolling.offset.y = end_pos;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
let overscroll_amount = scrolling.overscroll_amount(scrollable_width, scrollable_height);
|
||||
let overscrolling = CAN_OVERSCROLL && (overscroll_amount.x != 0.0 ||
|
||||
overscroll_amount.y != 0.0);
|
||||
let overscroll_amount = self.overscroll_amount();
|
||||
let overscrolling = CAN_OVERSCROLL && (overscroll_amount.width != 0.0 ||
|
||||
overscroll_amount.height != 0.0);
|
||||
if overscrolling {
|
||||
if overscroll_amount.x != 0.0 {
|
||||
delta.x /= overscroll_amount.x.abs()
|
||||
if overscroll_amount.width != 0.0 {
|
||||
delta.x /= overscroll_amount.width.abs()
|
||||
}
|
||||
if overscroll_amount.y != 0.0 {
|
||||
delta.y /= overscroll_amount.y.abs()
|
||||
if overscroll_amount.height != 0.0 {
|
||||
delta.y /= overscroll_amount.height.abs()
|
||||
}
|
||||
}
|
||||
|
||||
let scrollable_width = self.scrollable_width();
|
||||
let scrollable_height = self.scrollable_height();
|
||||
let is_unscrollable = scrollable_width <= 0. && scrollable_height <= 0.;
|
||||
let original_layer_scroll_offset = scrolling.offset;
|
||||
let original_layer_scroll_offset = self.scrolling.offset;
|
||||
|
||||
if scrollable_width > 0. {
|
||||
scrolling.offset.x = scrolling.offset.x + delta.x;
|
||||
self.scrolling.offset.x = self.scrolling.offset.x + delta.x;
|
||||
if is_unscrollable || !CAN_OVERSCROLL {
|
||||
scrolling.offset.x = scrolling.offset.x.min(0.0).max(-scrollable_width).round();
|
||||
self.scrolling.offset.x =
|
||||
self.scrolling.offset.x.min(0.0).max(-scrollable_width).round();
|
||||
}
|
||||
}
|
||||
|
||||
if scrollable_height > 0. {
|
||||
scrolling.offset.y = scrolling.offset.y + delta.y;
|
||||
self.scrolling.offset.y = self.scrolling.offset.y + delta.y;
|
||||
if is_unscrollable || !CAN_OVERSCROLL {
|
||||
scrolling.offset.y = scrolling.offset.y.min(0.0).max(-scrollable_height).round();
|
||||
self.scrolling.offset.y =
|
||||
self.scrolling.offset.y.min(0.0).max(-scrollable_height).round();
|
||||
}
|
||||
}
|
||||
|
||||
if phase == ScrollEventPhase::Start || phase == ScrollEventPhase::Move(true) {
|
||||
scrolling.started_bouncing_back = false
|
||||
self.scrolling.started_bouncing_back = false
|
||||
} else if overscrolling &&
|
||||
((delta.x < 1.0 && delta.y < 1.0) || phase == ScrollEventPhase::End) {
|
||||
scrolling.started_bouncing_back = true;
|
||||
scrolling.bouncing_back = true
|
||||
self.scrolling.started_bouncing_back = true;
|
||||
self.scrolling.bouncing_back = true
|
||||
}
|
||||
|
||||
if CAN_OVERSCROLL {
|
||||
scrolling.stretch_overscroll_spring(overscroll_amount);
|
||||
self.stretch_overscroll_spring();
|
||||
}
|
||||
|
||||
scrolling.offset != original_layer_scroll_offset || scrolling.started_bouncing_back
|
||||
self.scrolling.offset != original_layer_scroll_offset ||
|
||||
self.scrolling.started_bouncing_back
|
||||
}
|
||||
|
||||
pub fn stretch_overscroll_spring(&mut self) {
|
||||
let overscroll_amount = self.overscroll_amount();
|
||||
self.scrolling.spring.coords(self.scrolling.offset,
|
||||
self.scrolling.offset,
|
||||
self.scrolling.offset + overscroll_amount);
|
||||
}
|
||||
|
||||
pub fn tick_scrolling_bounce_animation(&mut self) {
|
||||
if let NodeType::ScrollFrame(ref mut scrolling) = self.node_type {
|
||||
scrolling.tick_scrolling_bounce_animation();
|
||||
let finished = self.scrolling.spring.animate();
|
||||
self.scrolling.offset = self.scrolling.spring.current();
|
||||
if finished {
|
||||
self.scrolling.bouncing_back = false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -400,89 +410,48 @@ impl ClipScrollNode {
|
|||
let z0 = -10000.0;
|
||||
let z1 = 10000.0;
|
||||
|
||||
let p0 = inv.transform_point3d(&cursor.extend(z0));
|
||||
let p1 = inv.transform_point3d(&cursor.extend(z1));
|
||||
let p0 = inv.transform_point4d(&WorldPoint4D::new(cursor.x, cursor.y, z0, 1.0));
|
||||
let p0 = Point3D::new(p0.x / p0.w,
|
||||
p0.y / p0.w,
|
||||
p0.z / p0.w);
|
||||
let p1 = inv.transform_point4d(&WorldPoint4D::new(cursor.x, cursor.y, z1, 1.0));
|
||||
let p1 = Point3D::new(p1.x / p1.w,
|
||||
p1.y / p1.w,
|
||||
p1.z / p1.w);
|
||||
|
||||
if self.scrollable_width() <= 0. && self.scrollable_height() <= 0. {
|
||||
return false;
|
||||
}
|
||||
ray_intersects_rect(p0.to_untyped(), p1.to_untyped(), self.local_viewport_rect.to_untyped())
|
||||
ray_intersects_rect(p0, p1, self.local_viewport_rect.to_untyped())
|
||||
}
|
||||
|
||||
pub fn scroll_offset(&self) -> LayerVector2D {
|
||||
pub fn scroll_offset(&self) -> Option<LayerPoint> {
|
||||
match self.node_type {
|
||||
NodeType::ScrollFrame(ref scrolling) => scrolling.offset,
|
||||
_ => LayerVector2D::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_overscrolling(&self) -> bool {
|
||||
match self.node_type {
|
||||
NodeType::ScrollFrame(ref scrolling) => {
|
||||
let overscroll_amount = scrolling.overscroll_amount(self.scrollable_width(),
|
||||
self.scrollable_height());
|
||||
overscroll_amount.x != 0.0 || overscroll_amount.y != 0.0
|
||||
}
|
||||
_ => false,
|
||||
NodeType::Clip(_) if self.scrollable_width() > 0. || self.scrollable_height() > 0. =>
|
||||
Some(self.scrolling.offset),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct ScrollingState {
|
||||
pub offset: LayerVector2D,
|
||||
pub offset: LayerPoint,
|
||||
pub spring: Spring,
|
||||
pub started_bouncing_back: bool,
|
||||
pub bouncing_back: bool,
|
||||
pub should_handoff_scroll: bool
|
||||
}
|
||||
|
||||
/// Manages scrolling offset, overscroll state, etc.
|
||||
impl ScrollingState {
|
||||
pub fn new() -> ScrollingState {
|
||||
ScrollingState {
|
||||
offset: LayerVector2D::zero(),
|
||||
offset: LayerPoint::zero(),
|
||||
spring: Spring::at(LayerPoint::zero(), STIFFNESS, DAMPING),
|
||||
started_bouncing_back: false,
|
||||
bouncing_back: false,
|
||||
should_handoff_scroll: false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stretch_overscroll_spring(&mut self, overscroll_amount: LayerVector2D) {
|
||||
let offset = self.offset.to_point();
|
||||
self.spring.coords(offset, offset, offset + overscroll_amount);
|
||||
}
|
||||
|
||||
pub fn tick_scrolling_bounce_animation(&mut self) {
|
||||
let finished = self.spring.animate();
|
||||
self.offset = self.spring.current().to_vector();
|
||||
if finished {
|
||||
self.bouncing_back = false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn overscroll_amount(&self,
|
||||
scrollable_width: f32,
|
||||
scrollable_height: f32)
|
||||
-> LayerVector2D {
|
||||
let overscroll_x = if self.offset.x > 0.0 {
|
||||
-self.offset.x
|
||||
} else if self.offset.x < -scrollable_width {
|
||||
-scrollable_width - self.offset.x
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let overscroll_y = if self.offset.y > 0.0 {
|
||||
-self.offset.y
|
||||
} else if self.offset.y < -scrollable_height {
|
||||
-scrollable_height - self.offset.y
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
LayerVector2D::new(overscroll_x, overscroll_y)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ use std::hash::BuildHasherDefault;
|
|||
use webrender_traits::{ClipId, LayerPoint, LayerRect, LayerToScrollTransform};
|
||||
use webrender_traits::{LayerToWorldTransform, PipelineId, ScrollClamping, ScrollEventPhase};
|
||||
use webrender_traits::{ScrollLayerRect, ScrollLayerState, ScrollLocation, WorldPoint};
|
||||
use webrender_traits::{as_scroll_parent_rect, LayerVector2D};
|
||||
use webrender_traits::as_scroll_parent_rect;
|
||||
|
||||
pub type ScrollStates = HashMap<ClipId, ScrollingState, BuildHasherDefault<FnvHasher>>;
|
||||
|
||||
|
@ -22,10 +22,10 @@ pub struct ClipScrollTree {
|
|||
/// node to scroll even if a touch operation leaves the boundaries of that node.
|
||||
pub currently_scrolling_node_id: Option<ClipId>,
|
||||
|
||||
/// The current frame id, used for giving a unique id to all new dynamically
|
||||
/// added frames and clips. The ClipScrollTree increments this by one every
|
||||
/// time a new dynamic frame is created.
|
||||
current_new_node_item: u64,
|
||||
/// The current reference frame id, used for giving a unique id to all new
|
||||
/// reference frames. The ClipScrollTree increments this by one every time a
|
||||
/// reference frame is created.
|
||||
current_reference_frame_id: u64,
|
||||
|
||||
/// The root reference frame, which is the true root of the ClipScrollTree. Initially
|
||||
/// this ID is not valid, which is indicated by ```node``` being empty.
|
||||
|
@ -49,7 +49,7 @@ impl ClipScrollTree {
|
|||
currently_scrolling_node_id: None,
|
||||
root_reference_frame_id: ClipId::root_reference_frame(dummy_pipeline),
|
||||
topmost_scrolling_node_id: ClipId::root_scroll_node(dummy_pipeline),
|
||||
current_new_node_item: 1,
|
||||
current_reference_frame_id: 0,
|
||||
pipelines_to_discard: HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
@ -72,10 +72,8 @@ impl ClipScrollTree {
|
|||
-> HashSet<ClipId, BuildHasherDefault<FnvHasher>> {
|
||||
let mut nodes_bouncing_back = HashSet::default();
|
||||
for (clip_id, node) in self.nodes.iter() {
|
||||
if let NodeType::ScrollFrame(ref scrolling) = node.node_type {
|
||||
if scrolling.bouncing_back {
|
||||
nodes_bouncing_back.insert(*clip_id);
|
||||
}
|
||||
if node.scrolling.bouncing_back {
|
||||
nodes_bouncing_back.insert(*clip_id);
|
||||
}
|
||||
}
|
||||
nodes_bouncing_back
|
||||
|
@ -87,15 +85,14 @@ impl ClipScrollTree {
|
|||
-> Option<ClipId> {
|
||||
self.nodes.get(&clip_id).and_then(|node| {
|
||||
for child_layer_id in node.children.iter().rev() {
|
||||
if let Some(layer_id) =
|
||||
self.find_scrolling_node_at_point_in_node(cursor, *child_layer_id) {
|
||||
if let Some(layer_id) =
|
||||
self.find_scrolling_node_at_point_in_node(cursor, *child_layer_id) {
|
||||
return Some(layer_id);
|
||||
}
|
||||
}
|
||||
|
||||
match node.node_type {
|
||||
NodeType::ScrollFrame(..) => {},
|
||||
_ => return None,
|
||||
if clip_id.is_reference_frame() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if node.ray_intersects_node(cursor) {
|
||||
|
@ -114,24 +111,21 @@ impl ClipScrollTree {
|
|||
pub fn get_scroll_node_state(&self) -> Vec<ScrollLayerState> {
|
||||
let mut result = vec![];
|
||||
for (id, node) in self.nodes.iter() {
|
||||
if let NodeType::ScrollFrame(scrolling) = node.node_type {
|
||||
result.push(ScrollLayerState { id: *id, scroll_offset: scrolling.offset })
|
||||
match node.scroll_offset() {
|
||||
Some(offset) => result.push(ScrollLayerState { id: *id, scroll_offset: offset }),
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn drain(&mut self) -> ScrollStates {
|
||||
self.current_new_node_item = 1;
|
||||
self.current_reference_frame_id = 1;
|
||||
|
||||
let mut scroll_states = HashMap::default();
|
||||
for (layer_id, old_node) in &mut self.nodes.drain() {
|
||||
if self.pipelines_to_discard.contains(&layer_id.pipeline_id()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let NodeType::ScrollFrame(scrolling) = old_node.node_type {
|
||||
scroll_states.insert(layer_id, scrolling);
|
||||
if !self.pipelines_to_discard.contains(&layer_id.pipeline_id()) {
|
||||
scroll_states.insert(layer_id, old_node.scrolling);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,6 +134,11 @@ impl ClipScrollTree {
|
|||
}
|
||||
|
||||
pub fn scroll_node(&mut self, origin: LayerPoint, id: ClipId, clamp: ScrollClamping) -> bool {
|
||||
if id.is_reference_frame() {
|
||||
warn!("Tried to scroll a reference frame.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.nodes.is_empty() {
|
||||
self.pending_scroll_offsets.insert(id, (origin, clamp));
|
||||
return false;
|
||||
|
@ -185,33 +184,37 @@ impl ClipScrollTree {
|
|||
|
||||
let topmost_scrolling_node_id = self.topmost_scrolling_node_id();
|
||||
let non_root_overscroll = if clip_id != topmost_scrolling_node_id {
|
||||
self.nodes.get(&clip_id).unwrap().is_overscrolling()
|
||||
// true if the current node is overscrolling,
|
||||
// and it is not the root scroll node.
|
||||
let child_node = self.nodes.get(&clip_id).unwrap();
|
||||
let overscroll_amount = child_node.overscroll_amount();
|
||||
overscroll_amount.width != 0.0 || overscroll_amount.height != 0.0
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let mut switch_node = false;
|
||||
if let Some(node) = self.nodes.get_mut(&clip_id) {
|
||||
if let NodeType::ScrollFrame(ref mut scrolling) = node.node_type {
|
||||
match phase {
|
||||
ScrollEventPhase::Start => {
|
||||
// if this is a new gesture, we do not switch node,
|
||||
// however we do save the state of non_root_overscroll,
|
||||
// for use in the subsequent Move phase.
|
||||
scrolling.should_handoff_scroll = non_root_overscroll;
|
||||
},
|
||||
ScrollEventPhase::Move(_) => {
|
||||
// Switch node if movement originated in a new gesture,
|
||||
// from a non root node in overscroll.
|
||||
switch_node = scrolling.should_handoff_scroll && non_root_overscroll
|
||||
},
|
||||
ScrollEventPhase::End => {
|
||||
// clean-up when gesture ends.
|
||||
scrolling.should_handoff_scroll = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let switch_node = match phase {
|
||||
ScrollEventPhase::Start => {
|
||||
// if this is a new gesture, we do not switch node,
|
||||
// however we do save the state of non_root_overscroll,
|
||||
// for use in the subsequent Move phase.
|
||||
let mut current_node = self.nodes.get_mut(&clip_id).unwrap();
|
||||
current_node.scrolling.should_handoff_scroll = non_root_overscroll;
|
||||
false
|
||||
},
|
||||
ScrollEventPhase::Move(_) => {
|
||||
// Switch node if movement originated in a new gesture,
|
||||
// from a non root node in overscroll.
|
||||
let current_node = self.nodes.get_mut(&clip_id).unwrap();
|
||||
current_node.scrolling.should_handoff_scroll && non_root_overscroll
|
||||
},
|
||||
ScrollEventPhase::End => {
|
||||
// clean-up when gesture ends.
|
||||
let mut current_node = self.nodes.get_mut(&clip_id).unwrap();
|
||||
current_node.scrolling.should_handoff_scroll = false;
|
||||
false
|
||||
},
|
||||
};
|
||||
|
||||
let clip_id = if switch_node {
|
||||
topmost_scrolling_node_id
|
||||
|
@ -232,16 +235,16 @@ impl ClipScrollTree {
|
|||
self.update_node_transform(root_reference_frame_id,
|
||||
&LayerToWorldTransform::create_translation(pan.x, pan.y, 0.0),
|
||||
&as_scroll_parent_rect(&root_viewport),
|
||||
LayerVector2D::zero(),
|
||||
LayerVector2D::zero());
|
||||
LayerPoint::zero(),
|
||||
LayerPoint::zero());
|
||||
}
|
||||
|
||||
fn update_node_transform(&mut self,
|
||||
layer_id: ClipId,
|
||||
parent_reference_frame_transform: &LayerToWorldTransform,
|
||||
parent_viewport_rect: &ScrollLayerRect,
|
||||
parent_scroll_offset: LayerVector2D,
|
||||
parent_accumulated_scroll_offset: LayerVector2D) {
|
||||
parent_scroll_offset: LayerPoint,
|
||||
parent_accumulated_scroll_offset: LayerPoint) {
|
||||
// TODO(gw): This is an ugly borrow check workaround to clone these.
|
||||
// Restructure this to avoid the clones!
|
||||
let (reference_frame_transform,
|
||||
|
@ -262,14 +265,11 @@ impl ClipScrollTree {
|
|||
// we need to reset both these values.
|
||||
let (transform, offset, accumulated_scroll_offset) = match node.node_type {
|
||||
NodeType::ReferenceFrame(..) =>
|
||||
(node.world_viewport_transform,
|
||||
LayerVector2D::zero(),
|
||||
LayerVector2D::zero()),
|
||||
_ => {
|
||||
let scroll_offset = node.scroll_offset();
|
||||
(node.world_viewport_transform, LayerPoint::zero(), LayerPoint::zero()),
|
||||
NodeType::Clip(_) => {
|
||||
(*parent_reference_frame_transform,
|
||||
scroll_offset,
|
||||
scroll_offset + parent_accumulated_scroll_offset)
|
||||
node.scrolling.offset,
|
||||
node.scrolling.offset + parent_accumulated_scroll_offset)
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -316,19 +316,17 @@ impl ClipScrollTree {
|
|||
|
||||
}
|
||||
|
||||
pub fn generate_new_clip_id(&mut self, pipeline_id: PipelineId) -> ClipId {
|
||||
let new_id = ClipId::DynamicallyAddedNode(self.current_new_node_item, pipeline_id);
|
||||
self.current_new_node_item += 1;
|
||||
new_id
|
||||
}
|
||||
|
||||
pub fn add_reference_frame(&mut self,
|
||||
rect: &LayerRect,
|
||||
transform: &LayerToScrollTransform,
|
||||
pipeline_id: PipelineId,
|
||||
parent_id: Option<ClipId>)
|
||||
-> ClipId {
|
||||
let reference_frame_id = self.generate_new_clip_id(pipeline_id);
|
||||
|
||||
let reference_frame_id =
|
||||
ClipId::ReferenceFrame(self.current_reference_frame_id, pipeline_id);
|
||||
self.current_reference_frame_id += 1;
|
||||
|
||||
let node = ClipScrollNode::new_reference_frame(parent_id,
|
||||
rect,
|
||||
rect.size,
|
||||
|
@ -375,13 +373,10 @@ impl ClipScrollTree {
|
|||
NodeType::ReferenceFrame(ref transform) => {
|
||||
pt.new_level(format!("ReferenceFrame {:?}", transform));
|
||||
}
|
||||
NodeType::ScrollFrame(scrolling_info) => {
|
||||
pt.new_level(format!("ScrollFrame"));
|
||||
pt.add_item(format!("scroll.offset: {:?}", scrolling_info.offset));
|
||||
}
|
||||
}
|
||||
|
||||
pt.add_item(format!("content_size: {:?}", node.content_size));
|
||||
pt.add_item(format!("scroll.offset: {:?}", node.scrolling.offset));
|
||||
pt.add_item(format!("combined_local_viewport_rect: {:?}", node.combined_local_viewport_rect));
|
||||
pt.add_item(format!("local_viewport_rect: {:?}", node.local_viewport_rect));
|
||||
pt.add_item(format!("local_clip_rect: {:?}", node.local_clip_rect));
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
use debug_font_data;
|
||||
use device::{Device, GpuMarker, ProgramId, VAOId, TextureId, VertexFormat};
|
||||
use device::{TextureFilter, VertexUsageHint, TextureTarget};
|
||||
use euclid::{Transform3D, Point2D, Size2D, Rect};
|
||||
use euclid::{Matrix4D, Point2D, Size2D, Rect};
|
||||
use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, TextureSampler};
|
||||
use internal_types::{DebugFontVertex, DebugColorVertex, RenderTargetMode, PackedColor};
|
||||
use std::f32;
|
||||
|
@ -166,12 +166,12 @@ impl DebugRenderer {
|
|||
device.set_blend(true);
|
||||
device.set_blend_mode_alpha();
|
||||
|
||||
let projection = Transform3D::ortho(0.0,
|
||||
viewport_size.width as f32,
|
||||
viewport_size.height as f32,
|
||||
0.0,
|
||||
ORTHO_NEAR_PLANE,
|
||||
ORTHO_FAR_PLANE);
|
||||
let projection = Matrix4D::ortho(0.0,
|
||||
viewport_size.width as f32,
|
||||
viewport_size.height as f32,
|
||||
0.0,
|
||||
ORTHO_NEAR_PLANE,
|
||||
ORTHO_FAR_PLANE);
|
||||
|
||||
// Triangles
|
||||
if !self.tri_vertices.is_empty() {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use euclid::Transform3D;
|
||||
use euclid::Matrix4D;
|
||||
use fnv::FnvHasher;
|
||||
use gleam::gl;
|
||||
use internal_types::{PackedVertex, RenderTargetMode, TextureSampler, DEFAULT_TEXTURE};
|
||||
|
@ -1059,7 +1059,7 @@ impl Device {
|
|||
|
||||
pub fn bind_program(&mut self,
|
||||
program_id: ProgramId,
|
||||
projection: &Transform3D<f32>) {
|
||||
projection: &Matrix4D<f32>) {
|
||||
debug_assert!(self.inside_frame);
|
||||
|
||||
if self.bound_program != program_id {
|
||||
|
@ -1554,6 +1554,16 @@ impl Device {
|
|||
self.gl.uniform_1i(u_tasks, TextureSampler::RenderTasks as i32);
|
||||
}
|
||||
|
||||
let u_prim_geom = self.gl.get_uniform_location(program.id, "sPrimGeometry");
|
||||
if u_prim_geom != -1 {
|
||||
self.gl.uniform_1i(u_prim_geom, TextureSampler::Geometry as i32);
|
||||
}
|
||||
|
||||
let u_data16 = self.gl.get_uniform_location(program.id, "sData16");
|
||||
if u_data16 != -1 {
|
||||
self.gl.uniform_1i(u_data16, TextureSampler::Data16 as i32);
|
||||
}
|
||||
|
||||
let u_data32 = self.gl.get_uniform_location(program.id, "sData32");
|
||||
if u_data32 != -1 {
|
||||
self.gl.uniform_1i(u_data32, TextureSampler::Data32 as i32);
|
||||
|
@ -1569,6 +1579,16 @@ impl Device {
|
|||
self.gl.uniform_1i(u_resource_rects, TextureSampler::ResourceRects as i32);
|
||||
}
|
||||
|
||||
let u_gradients = self.gl.get_uniform_location(program.id, "sGradients");
|
||||
if u_gradients != -1 {
|
||||
self.gl.uniform_1i(u_gradients, TextureSampler::Gradients as i32);
|
||||
}
|
||||
|
||||
let u_split_geometry = self.gl.get_uniform_location(program.id, "sSplitGeometry");
|
||||
if u_split_geometry != -1 {
|
||||
self.gl.uniform_1i(u_split_geometry, TextureSampler::SplitGeometry as i32);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1622,7 +1642,7 @@ impl Device {
|
|||
|
||||
fn set_uniforms(&self,
|
||||
program: &Program,
|
||||
transform: &Transform3D<f32>,
|
||||
transform: &Matrix4D<f32>,
|
||||
device_pixel_ratio: f32) {
|
||||
debug_assert!(self.inside_frame);
|
||||
self.gl.uniform_matrix_4fv(program.u_transform,
|
||||
|
|
|
@ -3,9 +3,8 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use app_units::Au;
|
||||
use euclid::rect;
|
||||
use euclid::rect::rect;
|
||||
use fnv::FnvHasher;
|
||||
use gpu_cache::GpuCache;
|
||||
use internal_types::{ANGLE_FLOAT_TO_FIXED, AxisDirection};
|
||||
use internal_types::{LowLevelFilterOp};
|
||||
use internal_types::{RendererFrame};
|
||||
|
@ -22,7 +21,7 @@ use util::{ComplexClipRegionHelpers, subtract_rect};
|
|||
use webrender_traits::{BuiltDisplayList, BuiltDisplayListIter, ClipAndScrollInfo, ClipDisplayItem};
|
||||
use webrender_traits::{ClipId, ClipRegion, ColorF, DeviceUintRect, DeviceUintSize, DisplayItemRef};
|
||||
use webrender_traits::{Epoch, FilterOp, ImageDisplayItem, ItemRange, LayerPoint, LayerRect};
|
||||
use webrender_traits::{LayerSize, LayerToScrollTransform, LayoutSize, LayoutTransform, LayerVector2D};
|
||||
use webrender_traits::{LayerSize, LayerToScrollTransform, LayoutSize, LayoutTransform};
|
||||
use webrender_traits::{MixBlendMode, PipelineId, ScrollClamping, ScrollEventPhase};
|
||||
use webrender_traits::{ScrollLayerState, ScrollLocation, ScrollPolicy, SpecificDisplayItem};
|
||||
use webrender_traits::{StackingContext, TileOffset, TransformStyle, WorldPoint};
|
||||
|
@ -91,23 +90,26 @@ impl StackingContextHelpers for StackingContext {
|
|||
properties: &SceneProperties) -> Vec<LowLevelFilterOp> {
|
||||
let mut filters = vec![];
|
||||
for filter in display_list.get(input_filters) {
|
||||
if filter.is_noop() {
|
||||
continue;
|
||||
}
|
||||
|
||||
match filter {
|
||||
FilterOp::Blur(radius) => {
|
||||
filters.push(LowLevelFilterOp::Blur(radius, AxisDirection::Horizontal));
|
||||
filters.push(LowLevelFilterOp::Blur(radius, AxisDirection::Vertical));
|
||||
filters.push(LowLevelFilterOp::Blur(
|
||||
radius,
|
||||
AxisDirection::Horizontal));
|
||||
filters.push(LowLevelFilterOp::Blur(
|
||||
radius,
|
||||
AxisDirection::Vertical));
|
||||
}
|
||||
FilterOp::Brightness(amount) => {
|
||||
filters.push(LowLevelFilterOp::Brightness(Au::from_f32_px(amount)));
|
||||
filters.push(
|
||||
LowLevelFilterOp::Brightness(Au::from_f32_px(amount)));
|
||||
}
|
||||
FilterOp::Contrast(amount) => {
|
||||
filters.push(LowLevelFilterOp::Contrast(Au::from_f32_px(amount)));
|
||||
filters.push(
|
||||
LowLevelFilterOp::Contrast(Au::from_f32_px(amount)));
|
||||
}
|
||||
FilterOp::Grayscale(amount) => {
|
||||
filters.push(LowLevelFilterOp::Grayscale(Au::from_f32_px(amount)));
|
||||
filters.push(
|
||||
LowLevelFilterOp::Grayscale(Au::from_f32_px(amount)));
|
||||
}
|
||||
FilterOp::HueRotate(angle) => {
|
||||
filters.push(
|
||||
|
@ -115,17 +117,21 @@ impl StackingContextHelpers for StackingContext {
|
|||
angle * ANGLE_FLOAT_TO_FIXED) as i32));
|
||||
}
|
||||
FilterOp::Invert(amount) => {
|
||||
filters.push(LowLevelFilterOp::Invert(Au::from_f32_px(amount)));
|
||||
filters.push(
|
||||
LowLevelFilterOp::Invert(Au::from_f32_px(amount)));
|
||||
}
|
||||
FilterOp::Opacity(ref value) => {
|
||||
let amount = properties.resolve_float(value, 1.0);
|
||||
filters.push(LowLevelFilterOp::Opacity(Au::from_f32_px(amount)));
|
||||
filters.push(
|
||||
LowLevelFilterOp::Opacity(Au::from_f32_px(amount)));
|
||||
}
|
||||
FilterOp::Saturate(amount) => {
|
||||
filters.push(LowLevelFilterOp::Saturate(Au::from_f32_px(amount)));
|
||||
filters.push(
|
||||
LowLevelFilterOp::Saturate(Au::from_f32_px(amount)));
|
||||
}
|
||||
FilterOp::Sepia(amount) => {
|
||||
filters.push(LowLevelFilterOp::Sepia(Au::from_f32_px(amount)));
|
||||
filters.push(
|
||||
LowLevelFilterOp::Sepia(Au::from_f32_px(amount)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -211,7 +217,8 @@ impl Frame {
|
|||
None => return,
|
||||
};
|
||||
|
||||
let display_list = match scene.display_lists.get(&root_pipeline_id) {
|
||||
let display_list = scene.display_lists.get(&root_pipeline_id);
|
||||
let display_list = match display_list {
|
||||
Some(display_list) => display_list,
|
||||
None => return,
|
||||
};
|
||||
|
@ -267,20 +274,12 @@ impl Frame {
|
|||
item: &ClipDisplayItem,
|
||||
content_rect: &LayerRect,
|
||||
clip: &ClipRegion) {
|
||||
let clip_viewport = LayerRect::new(content_rect.origin, clip.main.size);
|
||||
let new_clip_id = self.clip_scroll_tree.generate_new_clip_id(pipeline_id);
|
||||
context.builder.add_clip_scroll_node(new_clip_id,
|
||||
context.builder.add_clip_scroll_node(item.id,
|
||||
parent_id,
|
||||
pipeline_id,
|
||||
&clip_viewport,
|
||||
&content_rect,
|
||||
clip,
|
||||
&mut self.clip_scroll_tree);
|
||||
context.builder.add_scroll_frame(item.id,
|
||||
new_clip_id,
|
||||
pipeline_id,
|
||||
&content_rect,
|
||||
&clip_viewport,
|
||||
&mut self.clip_scroll_tree);
|
||||
|
||||
}
|
||||
|
||||
|
@ -289,7 +288,7 @@ impl Frame {
|
|||
pipeline_id: PipelineId,
|
||||
context: &mut FlattenContext,
|
||||
context_scroll_node_id: ClipId,
|
||||
mut reference_frame_relative_offset: LayerVector2D,
|
||||
mut reference_frame_relative_offset: LayerPoint,
|
||||
bounds: &LayerRect,
|
||||
stacking_context: &StackingContext,
|
||||
filters: ItemRange<FilterOp>) {
|
||||
|
@ -334,7 +333,7 @@ impl Frame {
|
|||
LayerToScrollTransform::create_translation(reference_frame_relative_offset.x,
|
||||
reference_frame_relative_offset.y,
|
||||
0.0)
|
||||
.pre_translate(bounds.origin.to_vector().to_3d())
|
||||
.pre_translated(bounds.origin.x, bounds.origin.y, 0.0)
|
||||
.pre_mul(&transform)
|
||||
.pre_mul(&perspective);
|
||||
|
||||
|
@ -345,9 +344,9 @@ impl Frame {
|
|||
&transform,
|
||||
&mut self.clip_scroll_tree);
|
||||
context.replacements.push((context_scroll_node_id, clip_id));
|
||||
reference_frame_relative_offset = LayerVector2D::zero();
|
||||
reference_frame_relative_offset = LayerPoint::zero();
|
||||
} else {
|
||||
reference_frame_relative_offset = LayerVector2D::new(
|
||||
reference_frame_relative_offset = LayerPoint::new(
|
||||
reference_frame_relative_offset.x + bounds.origin.x,
|
||||
reference_frame_relative_offset.y + bounds.origin.y);
|
||||
}
|
||||
|
@ -379,15 +378,15 @@ impl Frame {
|
|||
pipeline_id: PipelineId,
|
||||
parent_id: ClipId,
|
||||
bounds: &LayerRect,
|
||||
clip_region: &ClipRegion,
|
||||
context: &mut FlattenContext,
|
||||
reference_frame_relative_offset: LayerVector2D) {
|
||||
reference_frame_relative_offset: LayerPoint) {
|
||||
let pipeline = match context.scene.pipeline_map.get(&pipeline_id) {
|
||||
Some(pipeline) => pipeline,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let display_list = match context.scene.display_lists.get(&pipeline_id) {
|
||||
let display_list = context.scene.display_lists.get(&pipeline_id);
|
||||
let display_list = match display_list {
|
||||
Some(display_list) => display_list,
|
||||
None => return,
|
||||
};
|
||||
|
@ -400,27 +399,19 @@ impl Frame {
|
|||
reference_frame_relative_offset.y + bounds.origin.y,
|
||||
0.0);
|
||||
|
||||
let new_clip_id = self.clip_scroll_tree.generate_new_clip_id(pipeline_id);
|
||||
context.builder.add_clip_scroll_node(new_clip_id,
|
||||
parent_id,
|
||||
parent_id.pipeline_id(),
|
||||
bounds,
|
||||
clip_region,
|
||||
&mut self.clip_scroll_tree);
|
||||
|
||||
let iframe_reference_frame_id =
|
||||
context.builder.push_reference_frame(Some(new_clip_id),
|
||||
context.builder.push_reference_frame(Some(parent_id),
|
||||
pipeline_id,
|
||||
&iframe_rect,
|
||||
&transform,
|
||||
&mut self.clip_scroll_tree);
|
||||
|
||||
context.builder.add_scroll_frame(
|
||||
context.builder.add_clip_scroll_node(
|
||||
ClipId::root_scroll_node(pipeline_id),
|
||||
iframe_reference_frame_id,
|
||||
pipeline_id,
|
||||
&LayerRect::new(LayerPoint::zero(), pipeline.content_size),
|
||||
&iframe_rect,
|
||||
&ClipRegion::simple(&iframe_rect),
|
||||
&mut self.clip_scroll_tree);
|
||||
|
||||
self.flatten_root(&mut display_list.iter(), pipeline_id, context, &pipeline.content_size);
|
||||
|
@ -432,7 +423,7 @@ impl Frame {
|
|||
item: DisplayItemRef<'a, 'b>,
|
||||
pipeline_id: PipelineId,
|
||||
context: &mut FlattenContext,
|
||||
reference_frame_relative_offset: LayerVector2D)
|
||||
reference_frame_relative_offset: LayerPoint)
|
||||
-> Option<BuiltDisplayListIter<'a>> {
|
||||
let mut clip_and_scroll = item.clip_and_scroll();
|
||||
clip_and_scroll.scroll_node_id =
|
||||
|
@ -492,10 +483,13 @@ impl Frame {
|
|||
text_info.glyph_options);
|
||||
}
|
||||
SpecificDisplayItem::Rectangle(ref info) => {
|
||||
let display_list = context.scene.display_lists
|
||||
.get(&pipeline_id)
|
||||
.expect("No display list?!");
|
||||
// Try to extract the opaque inner rectangle out of the clipped primitive.
|
||||
if let Some(opaque_rect) = clip_intersection(&item.rect(),
|
||||
item.clip_region(),
|
||||
item.display_list()) {
|
||||
display_list) {
|
||||
let mut results = Vec::new();
|
||||
subtract_rect(&item.rect(), &opaque_rect, &mut results);
|
||||
// The inner rectangle is considered opaque within this layer.
|
||||
|
@ -583,7 +577,6 @@ impl Frame {
|
|||
self.flatten_iframe(info.pipeline_id,
|
||||
clip_and_scroll.scroll_node_id,
|
||||
&item.rect(),
|
||||
&item.clip_region(),
|
||||
context,
|
||||
reference_frame_relative_offset);
|
||||
}
|
||||
|
@ -612,7 +605,7 @@ impl Frame {
|
|||
context: &mut FlattenContext,
|
||||
content_size: &LayoutSize) {
|
||||
let root_bounds = LayerRect::new(LayerPoint::zero(), *content_size);
|
||||
context.builder.push_stacking_context(&LayerVector2D::zero(),
|
||||
context.builder.push_stacking_context(&LayerPoint::zero(),
|
||||
pipeline_id,
|
||||
CompositeOps::default(),
|
||||
root_bounds,
|
||||
|
@ -627,7 +620,7 @@ impl Frame {
|
|||
// For the root pipeline, there's no need to add a full screen rectangle
|
||||
// here, as it's handled by the framebuffer clear.
|
||||
let clip_id = ClipId::root_scroll_node(pipeline_id);
|
||||
if context.scene.root_pipeline_id != Some(pipeline_id) {
|
||||
if context.scene.root_pipeline_id.unwrap() != pipeline_id {
|
||||
if let Some(pipeline) = context.scene.pipeline_map.get(&pipeline_id) {
|
||||
if let Some(bg_color) = pipeline.background_color {
|
||||
context.builder.add_solid_rectangle(ClipAndScrollInfo::simple(clip_id),
|
||||
|
@ -640,7 +633,7 @@ impl Frame {
|
|||
}
|
||||
|
||||
|
||||
self.flatten_items(traversal, pipeline_id, context, LayerVector2D::zero());
|
||||
self.flatten_items(traversal, pipeline_id, context, LayerPoint::zero());
|
||||
|
||||
if self.frame_builder_config.enable_scrollbars {
|
||||
let scrollbar_rect = LayerRect::new(LayerPoint::zero(), LayerSize::new(10.0, 70.0));
|
||||
|
@ -659,7 +652,7 @@ impl Frame {
|
|||
traversal: &mut BuiltDisplayListIter<'a>,
|
||||
pipeline_id: PipelineId,
|
||||
context: &mut FlattenContext,
|
||||
reference_frame_relative_offset: LayerVector2D) {
|
||||
reference_frame_relative_offset: LayerPoint) {
|
||||
loop {
|
||||
let subtraversal = {
|
||||
let item = match traversal.next() {
|
||||
|
@ -942,7 +935,7 @@ impl Frame {
|
|||
);
|
||||
|
||||
let mut prim_rect = LayerRect::new(
|
||||
item_rect.origin + LayerVector2D::new(
|
||||
item_rect.origin + LayerPoint::new(
|
||||
tile_offset.x as f32 * stretched_tile_size.width,
|
||||
tile_offset.y as f32 * stretched_tile_size.height,
|
||||
),
|
||||
|
@ -975,7 +968,6 @@ impl Frame {
|
|||
|
||||
pub fn build(&mut self,
|
||||
resource_cache: &mut ResourceCache,
|
||||
gpu_cache: &mut GpuCache,
|
||||
display_lists: &DisplayListMap,
|
||||
device_pixel_ratio: f32,
|
||||
pan: LayerPoint,
|
||||
|
@ -984,7 +976,6 @@ impl Frame {
|
|||
-> RendererFrame {
|
||||
self.clip_scroll_tree.update_all_node_transforms(pan);
|
||||
let frame = self.build_frame(resource_cache,
|
||||
gpu_cache,
|
||||
display_lists,
|
||||
device_pixel_ratio,
|
||||
texture_cache_profile,
|
||||
|
@ -998,7 +989,6 @@ impl Frame {
|
|||
|
||||
fn build_frame(&mut self,
|
||||
resource_cache: &mut ResourceCache,
|
||||
gpu_cache: &mut GpuCache,
|
||||
display_lists: &DisplayListMap,
|
||||
device_pixel_ratio: f32,
|
||||
texture_cache_profile: &mut TextureCacheProfileCounters,
|
||||
|
@ -1007,7 +997,6 @@ impl Frame {
|
|||
let mut frame_builder = self.frame_builder.take();
|
||||
let frame = frame_builder.as_mut().map(|builder|
|
||||
builder.build(resource_cache,
|
||||
gpu_cache,
|
||||
self.id,
|
||||
&mut self.clip_scroll_tree,
|
||||
display_lists,
|
||||
|
|
|
@ -4,15 +4,14 @@
|
|||
|
||||
use app_units::Au;
|
||||
use frame::FrameId;
|
||||
use gpu_cache::GpuCache;
|
||||
use gpu_store::GpuStoreAddress;
|
||||
use internal_types::{HardwareCompositeOp, SourceTexture};
|
||||
use mask_cache::{ClipMode, ClipSource, MaskCacheInfo, RegionMode};
|
||||
use plane_split::{BspSplitter, Polygon, Splitter};
|
||||
use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu};
|
||||
use prim_store::{ImagePrimitiveKind, PrimitiveContainer, PrimitiveIndex};
|
||||
use prim_store::{ImagePrimitiveKind, PrimitiveContainer, PrimitiveGeometry, PrimitiveIndex};
|
||||
use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
|
||||
use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu};
|
||||
use prim_store::{RectanglePrimitive, SplitGeometry, TextRunPrimitiveCpu};
|
||||
use prim_store::{BoxShadowPrimitiveCpu, TexelRect, YuvImagePrimitiveCpu};
|
||||
use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
|
||||
use render_task::{AlphaRenderItem, MaskCacheKey, MaskResult, RenderTask, RenderTaskIndex};
|
||||
|
@ -22,7 +21,7 @@ use clip_scroll_node::{ClipInfo, ClipScrollNode, NodeType};
|
|||
use clip_scroll_tree::ClipScrollTree;
|
||||
use std::{cmp, f32, i32, mem, usize};
|
||||
use std::collections::HashMap;
|
||||
use euclid::{SideOffsets2D, vec2, vec3};
|
||||
use euclid::{SideOffsets2D, TypedPoint3D};
|
||||
use tiling::{ContextIsolation, StackingContextIndex};
|
||||
use tiling::{ClipScrollGroup, ClipScrollGroupIndex, CompositeOps, DisplayListMap, Frame};
|
||||
use tiling::{PackedLayer, PackedLayerIndex, PrimitiveFlags, PrimitiveRunCmd, RenderPass};
|
||||
|
@ -35,7 +34,7 @@ use webrender_traits::{DeviceUintRect, DeviceUintSize, ExtendMode, FontKey, Font
|
|||
use webrender_traits::{GlyphInstance, GlyphOptions, GradientStop, ImageKey, ImageRendering};
|
||||
use webrender_traits::{ItemRange, LayerPoint, LayerRect, LayerSize, LayerToScrollTransform};
|
||||
use webrender_traits::{PipelineId, RepeatMode, TileOffset, TransformStyle, WebGLContextId};
|
||||
use webrender_traits::{WorldPixel, YuvColorSpace, YuvData, LayerVector2D};
|
||||
use webrender_traits::{WorldPixel, YuvColorSpace, YuvData};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct ImageBorderSegment {
|
||||
|
@ -96,7 +95,7 @@ fn make_polygon(sc: &StackingContext, node: &ClipScrollNode, anchor: usize)
|
|||
// Which in turn needs it to be a render task property obeyed by all primitives
|
||||
// upon rendering, possibly not limited to `write_*_vertex` implementations.
|
||||
let size = sc.local_bounds.bottom_right();
|
||||
let bounds = LayerRect::new(sc.reference_frame_offset.to_point(), LayerSize::new(size.x, size.y));
|
||||
let bounds = LayerRect::new(sc.reference_frame_offset, LayerSize::new(size.x, size.y));
|
||||
Polygon::from_transformed_rect(bounds, node.world_content_transform, anchor)
|
||||
}
|
||||
|
||||
|
@ -198,6 +197,10 @@ impl FrameBuilder {
|
|||
|
||||
self.create_clip_scroll_group_if_necessary(stacking_context_index, clip_and_scroll);
|
||||
|
||||
let geometry = PrimitiveGeometry {
|
||||
local_rect: *rect,
|
||||
local_clip_rect: clip_region.main,
|
||||
};
|
||||
let mut clip_sources = Vec::new();
|
||||
if clip_region.is_complex() {
|
||||
clip_sources.push(ClipSource::Region(clip_region.clone(), RegionMode::ExcludeRect));
|
||||
|
@ -208,8 +211,7 @@ impl FrameBuilder {
|
|||
let clip_info = MaskCacheInfo::new(&clip_sources,
|
||||
&mut self.prim_store.gpu_data32);
|
||||
|
||||
let prim_index = self.prim_store.add_primitive(rect,
|
||||
&clip_region.main,
|
||||
let prim_index = self.prim_store.add_primitive(geometry,
|
||||
clip_sources,
|
||||
clip_info,
|
||||
container);
|
||||
|
@ -253,7 +255,7 @@ impl FrameBuilder {
|
|||
}
|
||||
|
||||
pub fn push_stacking_context(&mut self,
|
||||
reference_frame_offset: &LayerVector2D,
|
||||
reference_frame_offset: &LayerPoint,
|
||||
pipeline_id: PipelineId,
|
||||
composite_ops: CompositeOps,
|
||||
local_bounds: LayerRect,
|
||||
|
@ -353,14 +355,12 @@ impl FrameBuilder {
|
|||
|
||||
let topmost_scrolling_node_id = ClipId::root_scroll_node(pipeline_id);
|
||||
clip_scroll_tree.topmost_scrolling_node_id = topmost_scrolling_node_id;
|
||||
|
||||
self.add_scroll_frame(topmost_scrolling_node_id,
|
||||
clip_scroll_tree.root_reference_frame_id,
|
||||
pipeline_id,
|
||||
&LayerRect::new(LayerPoint::zero(), *content_size),
|
||||
&viewport_rect,
|
||||
clip_scroll_tree);
|
||||
|
||||
self.add_clip_scroll_node(topmost_scrolling_node_id,
|
||||
clip_scroll_tree.root_reference_frame_id,
|
||||
pipeline_id,
|
||||
&LayerRect::new(LayerPoint::zero(), *content_size),
|
||||
&ClipRegion::simple(&viewport_rect),
|
||||
clip_scroll_tree);
|
||||
topmost_scrolling_node_id
|
||||
}
|
||||
|
||||
|
@ -384,21 +384,6 @@ impl FrameBuilder {
|
|||
self.packed_layers.push(PackedLayer::empty());
|
||||
}
|
||||
|
||||
pub fn add_scroll_frame(&mut self,
|
||||
new_node_id: ClipId,
|
||||
parent_id: ClipId,
|
||||
pipeline_id: PipelineId,
|
||||
content_rect: &LayerRect,
|
||||
frame_rect: &LayerRect,
|
||||
clip_scroll_tree: &mut ClipScrollTree) {
|
||||
let node = ClipScrollNode::new_scroll_frame(pipeline_id,
|
||||
parent_id,
|
||||
content_rect,
|
||||
frame_rect);
|
||||
|
||||
clip_scroll_tree.add_node(node, new_node_id);
|
||||
}
|
||||
|
||||
pub fn pop_reference_frame(&mut self) {
|
||||
self.reference_frame_stack.pop();
|
||||
}
|
||||
|
@ -451,17 +436,17 @@ impl FrameBuilder {
|
|||
let rect = LayerRect::new(origin, size);
|
||||
|
||||
let tl_outer = LayerPoint::new(rect.origin.x, rect.origin.y);
|
||||
let tl_inner = tl_outer + vec2(border_item.widths.left, border_item.widths.top);
|
||||
let tl_inner = tl_outer + LayerPoint::new(border_item.widths.left, border_item.widths.top);
|
||||
|
||||
let tr_outer = LayerPoint::new(rect.origin.x + rect.size.width, rect.origin.y);
|
||||
let tr_inner = tr_outer + vec2(-border_item.widths.right, border_item.widths.top);
|
||||
let tr_inner = tr_outer + LayerPoint::new(-border_item.widths.right, border_item.widths.top);
|
||||
|
||||
let bl_outer = LayerPoint::new(rect.origin.x, rect.origin.y + rect.size.height);
|
||||
let bl_inner = bl_outer + vec2(border_item.widths.left, -border_item.widths.bottom);
|
||||
let bl_inner = bl_outer + LayerPoint::new(border_item.widths.left, -border_item.widths.bottom);
|
||||
|
||||
let br_outer = LayerPoint::new(rect.origin.x + rect.size.width,
|
||||
rect.origin.y + rect.size.height);
|
||||
let br_inner = br_outer - vec2(border_item.widths.right, border_item.widths.bottom);
|
||||
let br_inner = br_outer - LayerPoint::new(border_item.widths.right, border_item.widths.bottom);
|
||||
|
||||
// Build the list of gradient segments
|
||||
vec![
|
||||
|
@ -505,17 +490,17 @@ impl FrameBuilder {
|
|||
let py3 = border.patch.height;
|
||||
|
||||
let tl_outer = LayerPoint::new(rect.origin.x, rect.origin.y);
|
||||
let tl_inner = tl_outer + vec2(border_item.widths.left, border_item.widths.top);
|
||||
let tl_inner = tl_outer + LayerPoint::new(border_item.widths.left, border_item.widths.top);
|
||||
|
||||
let tr_outer = LayerPoint::new(rect.origin.x + rect.size.width, rect.origin.y);
|
||||
let tr_inner = tr_outer + vec2(-border_item.widths.right, border_item.widths.top);
|
||||
let tr_inner = tr_outer + LayerPoint::new(-border_item.widths.right, border_item.widths.top);
|
||||
|
||||
let bl_outer = LayerPoint::new(rect.origin.x, rect.origin.y + rect.size.height);
|
||||
let bl_inner = bl_outer + vec2(border_item.widths.left, -border_item.widths.bottom);
|
||||
let bl_inner = bl_outer + LayerPoint::new(border_item.widths.left, -border_item.widths.bottom);
|
||||
|
||||
let br_outer = LayerPoint::new(rect.origin.x + rect.size.width,
|
||||
rect.origin.y + rect.size.height);
|
||||
let br_inner = br_outer - vec2(border_item.widths.right, border_item.widths.bottom);
|
||||
let br_inner = br_outer - LayerPoint::new(border_item.widths.right, border_item.widths.bottom);
|
||||
|
||||
// Build the list of image segments
|
||||
let mut segments = vec![
|
||||
|
@ -679,6 +664,9 @@ impl FrameBuilder {
|
|||
stops_count: stops_count,
|
||||
extend_mode: extend_mode,
|
||||
reverse_stops: reverse_stops,
|
||||
cache_dirty: true,
|
||||
gpu_data_address: GpuStoreAddress(0),
|
||||
gpu_data_count: 0,
|
||||
gpu_blocks: [
|
||||
[sp.x, sp.y, ep.x, ep.y].into(),
|
||||
[tile_size.width, tile_size.height, tile_repeat.width, tile_repeat.height].into(),
|
||||
|
@ -713,6 +701,7 @@ impl FrameBuilder {
|
|||
let radial_gradient_cpu = RadialGradientPrimitiveCpu {
|
||||
stops_range: stops,
|
||||
extend_mode: extend_mode,
|
||||
cache_dirty: true,
|
||||
gpu_data_address: GpuStoreAddress(0),
|
||||
gpu_data_count: 0,
|
||||
gpu_blocks: [
|
||||
|
@ -783,12 +772,15 @@ impl FrameBuilder {
|
|||
blur_radius: blur_radius,
|
||||
glyph_range: glyph_range,
|
||||
glyph_count: glyph_count,
|
||||
cache_dirty: true,
|
||||
glyph_instances: Vec::new(),
|
||||
color_texture_id: SourceTexture::Invalid,
|
||||
color: *color,
|
||||
render_mode: render_mode,
|
||||
glyph_options: glyph_options,
|
||||
resource_address: GpuStoreAddress(0),
|
||||
gpu_data_address: GpuStoreAddress(0),
|
||||
gpu_data_count: 0,
|
||||
};
|
||||
|
||||
self.add_primitive(clip_and_scroll,
|
||||
|
@ -839,7 +831,7 @@ impl FrameBuilder {
|
|||
clip_and_scroll: ClipAndScrollInfo,
|
||||
box_bounds: &LayerRect,
|
||||
clip_region: &ClipRegion,
|
||||
box_offset: &LayerVector2D,
|
||||
box_offset: &LayerPoint,
|
||||
color: &ColorF,
|
||||
blur_radius: f32,
|
||||
spread_radius: f32,
|
||||
|
@ -1098,7 +1090,6 @@ impl FrameBuilder {
|
|||
clip_scroll_tree: &mut ClipScrollTree,
|
||||
display_lists: &DisplayListMap,
|
||||
resource_cache: &mut ResourceCache,
|
||||
gpu_cache: &mut GpuCache,
|
||||
profile_counters: &mut FrameProfileCounters,
|
||||
device_pixel_ratio: f32) {
|
||||
profile_scope!("cull");
|
||||
|
@ -1107,61 +1098,56 @@ impl FrameBuilder {
|
|||
clip_scroll_tree,
|
||||
display_lists,
|
||||
resource_cache,
|
||||
gpu_cache,
|
||||
profile_counters,
|
||||
device_pixel_ratio);
|
||||
}
|
||||
|
||||
fn update_scroll_bars(&mut self,
|
||||
clip_scroll_tree: &ClipScrollTree,
|
||||
gpu_cache: &mut GpuCache) {
|
||||
fn update_scroll_bars(&mut self, clip_scroll_tree: &ClipScrollTree) {
|
||||
let distance_from_edge = 8.0;
|
||||
|
||||
for scrollbar_prim in &self.scrollbar_prims {
|
||||
let metadata = &mut self.prim_store.cpu_metadata[scrollbar_prim.prim_index.0];
|
||||
let mut geom = (*self.prim_store.gpu_geometry.get(GpuStoreAddress(scrollbar_prim.prim_index.0 as i32))).clone();
|
||||
let clip_scroll_node = &clip_scroll_tree.nodes[&scrollbar_prim.clip_id];
|
||||
|
||||
// Invalidate what's in the cache so it will get rebuilt.
|
||||
gpu_cache.invalidate(&metadata.gpu_location);
|
||||
|
||||
let scrollable_distance = clip_scroll_node.scrollable_height();
|
||||
|
||||
if scrollable_distance <= 0.0 {
|
||||
metadata.local_clip_rect.size = LayerSize::zero();
|
||||
geom.local_clip_rect.size = LayerSize::zero();
|
||||
*self.prim_store.gpu_geometry.get_mut(GpuStoreAddress(scrollbar_prim.prim_index.0 as i32)) = geom;
|
||||
continue;
|
||||
}
|
||||
|
||||
let scroll_offset = clip_scroll_node.scroll_offset();
|
||||
let f = -scroll_offset.y / scrollable_distance;
|
||||
let f = -clip_scroll_node.scrolling.offset.y / scrollable_distance;
|
||||
|
||||
let min_y = clip_scroll_node.local_viewport_rect.origin.y -
|
||||
scroll_offset.y +
|
||||
clip_scroll_node.scrolling.offset.y +
|
||||
distance_from_edge;
|
||||
|
||||
let max_y = clip_scroll_node.local_viewport_rect.origin.y +
|
||||
clip_scroll_node.local_viewport_rect.size.height -
|
||||
scroll_offset.y -
|
||||
metadata.local_rect.size.height -
|
||||
clip_scroll_node.scrolling.offset.y -
|
||||
geom.local_rect.size.height -
|
||||
distance_from_edge;
|
||||
|
||||
metadata.local_rect.origin.x = clip_scroll_node.local_viewport_rect.origin.x +
|
||||
clip_scroll_node.local_viewport_rect.size.width -
|
||||
metadata.local_rect.size.width -
|
||||
distance_from_edge;
|
||||
geom.local_rect.origin.x = clip_scroll_node.local_viewport_rect.origin.x +
|
||||
clip_scroll_node.local_viewport_rect.size.width -
|
||||
geom.local_rect.size.width -
|
||||
distance_from_edge;
|
||||
|
||||
metadata.local_rect.origin.y = util::lerp(min_y, max_y, f);
|
||||
metadata.local_clip_rect = metadata.local_rect;
|
||||
geom.local_rect.origin.y = util::lerp(min_y, max_y, f);
|
||||
geom.local_clip_rect = geom.local_rect;
|
||||
|
||||
// TODO(gw): The code to set / update border clips on scroll bars
|
||||
// has been broken for a long time, so I've removed it
|
||||
// for now. We can re-add that code once the clips
|
||||
// data is moved over to the GPU cache!
|
||||
let clip_source = if scrollbar_prim.border_radius > 0.0 {
|
||||
Some(ClipSource::Complex(geom.local_rect, scrollbar_prim.border_radius, ClipMode::Clip))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.prim_store.set_clip_source(scrollbar_prim.prim_index, clip_source);
|
||||
*self.prim_store.gpu_geometry.get_mut(GpuStoreAddress(scrollbar_prim.prim_index.0 as i32)) = geom;
|
||||
}
|
||||
}
|
||||
|
||||
fn build_render_task(&mut self,
|
||||
clip_scroll_tree: &ClipScrollTree,
|
||||
gpu_cache: &mut GpuCache)
|
||||
fn build_render_task(&mut self, clip_scroll_tree: &ClipScrollTree)
|
||||
-> (RenderTask, usize) {
|
||||
profile_scope!("build_render_task");
|
||||
|
||||
|
@ -1185,6 +1171,7 @@ impl FrameBuilder {
|
|||
// The plane splitter, using a simple BSP tree.
|
||||
let mut splitter = BspSplitter::new();
|
||||
|
||||
self.prim_store.gpu_split_geometry.clear();
|
||||
debug!("build_render_task()");
|
||||
|
||||
for cmd in &self.cmds {
|
||||
|
@ -1302,18 +1289,19 @@ impl FrameBuilder {
|
|||
current_task.children.extend(preserve_3d_map.values().cloned());
|
||||
debug!("\tplane splitting in {:?}", current_task.id);
|
||||
// Z axis is directed at the screen, `sort` is ascending, and we need back-to-front order.
|
||||
for poly in splitter.sort(vec3(0.0, 0.0, 1.0)) {
|
||||
for poly in splitter.sort(TypedPoint3D::new(0.0, 0.0, 1.0)) {
|
||||
let sc_index = StackingContextIndex(poly.anchor);
|
||||
let task_id = preserve_3d_map[&sc_index].id;
|
||||
debug!("\t\tproduce {:?} -> {:?} for {:?}", sc_index, poly, task_id);
|
||||
let pp = &poly.points;
|
||||
let gpu_blocks = [
|
||||
[pp[0].x, pp[0].y, pp[0].z, pp[1].x].into(),
|
||||
[pp[1].y, pp[1].z, pp[2].x, pp[2].y].into(),
|
||||
[pp[2].z, pp[3].x, pp[3].y, pp[3].z].into(),
|
||||
];
|
||||
let handle = gpu_cache.push_per_frame_blocks(&gpu_blocks);
|
||||
let item = AlphaRenderItem::SplitComposite(sc_index, task_id, handle, next_z);
|
||||
let split_geo = SplitGeometry {
|
||||
data: [pp[0].x, pp[0].y, pp[0].z,
|
||||
pp[1].x, pp[1].y, pp[1].z,
|
||||
pp[2].x, pp[2].y, pp[2].z,
|
||||
pp[3].x, pp[3].y, pp[3].z],
|
||||
};
|
||||
let gpu_index = self.prim_store.gpu_split_geometry.push(split_geo);
|
||||
let item = AlphaRenderItem::SplitComposite(sc_index, task_id, gpu_index, next_z);
|
||||
current_task.as_alpha_batch().items.push(item);
|
||||
}
|
||||
splitter.reset();
|
||||
|
@ -1369,7 +1357,6 @@ impl FrameBuilder {
|
|||
|
||||
pub fn build(&mut self,
|
||||
resource_cache: &mut ResourceCache,
|
||||
gpu_cache: &mut GpuCache,
|
||||
frame_id: FrameId,
|
||||
clip_scroll_tree: &mut ClipScrollTree,
|
||||
display_lists: &DisplayListMap,
|
||||
|
@ -1383,7 +1370,6 @@ impl FrameBuilder {
|
|||
profile_counters.total_primitives.set(self.prim_store.prim_count());
|
||||
|
||||
resource_cache.begin_frame(frame_id);
|
||||
gpu_cache.begin_frame();
|
||||
|
||||
let screen_rect = DeviceIntRect::new(
|
||||
DeviceIntPoint::zero(),
|
||||
|
@ -1397,17 +1383,16 @@ impl FrameBuilder {
|
|||
let cache_size = DeviceUintSize::new(cmp::max(1024, screen_rect.size.width as u32),
|
||||
cmp::max(1024, screen_rect.size.height as u32));
|
||||
|
||||
self.update_scroll_bars(clip_scroll_tree, gpu_cache);
|
||||
self.update_scroll_bars(clip_scroll_tree);
|
||||
|
||||
self.build_layer_screen_rects_and_cull_layers(&screen_rect,
|
||||
clip_scroll_tree,
|
||||
display_lists,
|
||||
resource_cache,
|
||||
gpu_cache,
|
||||
&mut profile_counters,
|
||||
device_pixel_ratio);
|
||||
|
||||
let (main_render_task, static_render_task_count) = self.build_render_task(clip_scroll_tree, gpu_cache);
|
||||
let (main_render_task, static_render_task_count) = self.build_render_task(clip_scroll_tree);
|
||||
let mut render_tasks = RenderTaskCollection::new(static_render_task_count);
|
||||
|
||||
let mut required_pass_count = 0;
|
||||
|
@ -1426,7 +1411,8 @@ impl FrameBuilder {
|
|||
let deferred_resolves = self.prim_store.resolve_primitives(resource_cache,
|
||||
device_pixel_ratio);
|
||||
|
||||
let gpu_cache_updates = gpu_cache.end_frame(gpu_cache_profile);
|
||||
let gpu_cache_updates = resource_cache.gpu_cache
|
||||
.end_frame(gpu_cache_profile);
|
||||
|
||||
let mut passes = Vec::new();
|
||||
|
||||
|
@ -1446,7 +1432,6 @@ impl FrameBuilder {
|
|||
clip_scroll_group_store: &self.clip_scroll_group_store,
|
||||
prim_store: &self.prim_store,
|
||||
resource_cache: resource_cache,
|
||||
gpu_cache: gpu_cache,
|
||||
};
|
||||
|
||||
pass.build(&ctx, &mut render_tasks);
|
||||
|
@ -1467,7 +1452,11 @@ impl FrameBuilder {
|
|||
cache_size: cache_size,
|
||||
layer_texture_data: self.packed_layers.clone(),
|
||||
render_task_data: render_tasks.render_task_data,
|
||||
gpu_data16: self.prim_store.gpu_data16.build(),
|
||||
gpu_data32: self.prim_store.gpu_data32.build(),
|
||||
gpu_geometry: self.prim_store.gpu_geometry.build(),
|
||||
gpu_gradient_data: self.prim_store.gpu_gradient_data.build(),
|
||||
gpu_split_geometry: self.prim_store.gpu_split_geometry.build(),
|
||||
gpu_resource_rects: self.prim_store.gpu_resource_rects.build(),
|
||||
deferred_resolves: deferred_resolves,
|
||||
gpu_cache_updates: Some(gpu_cache_updates),
|
||||
|
@ -1482,7 +1471,6 @@ struct LayerRectCalculationAndCullingPass<'a> {
|
|||
clip_scroll_tree: &'a mut ClipScrollTree,
|
||||
display_lists: &'a DisplayListMap,
|
||||
resource_cache: &'a mut ResourceCache,
|
||||
gpu_cache: &'a mut GpuCache,
|
||||
profile_counters: &'a mut FrameProfileCounters,
|
||||
device_pixel_ratio: f32,
|
||||
stacking_context_stack: Vec<StackingContextIndex>,
|
||||
|
@ -1503,7 +1491,6 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> {
|
|||
clip_scroll_tree: &'a mut ClipScrollTree,
|
||||
display_lists: &'a DisplayListMap,
|
||||
resource_cache: &'a mut ResourceCache,
|
||||
gpu_cache: &'a mut GpuCache,
|
||||
profile_counters: &'a mut FrameProfileCounters,
|
||||
device_pixel_ratio: f32) {
|
||||
|
||||
|
@ -1513,7 +1500,6 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> {
|
|||
clip_scroll_tree: clip_scroll_tree,
|
||||
display_lists: display_lists,
|
||||
resource_cache: resource_cache,
|
||||
gpu_cache: gpu_cache,
|
||||
profile_counters: profile_counters,
|
||||
device_pixel_ratio: device_pixel_ratio,
|
||||
stacking_context_stack: Vec::new(),
|
||||
|
@ -1546,7 +1532,7 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> {
|
|||
for (_, ref mut node) in self.clip_scroll_tree.nodes.iter_mut() {
|
||||
let node_clip_info = match node.node_type {
|
||||
NodeType::Clip(ref mut clip_info) => clip_info,
|
||||
_ => continue,
|
||||
NodeType::ReferenceFrame(_) => continue,
|
||||
};
|
||||
|
||||
let packed_layer_index = node_clip_info.packed_layer_index;
|
||||
|
@ -1556,13 +1542,15 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> {
|
|||
// so we need to account for that origin in the transformation we assign to
|
||||
// the packed layer.
|
||||
let transform = node.world_viewport_transform
|
||||
.pre_translate(node.local_viewport_rect.origin.to_vector().to_3d());
|
||||
.pre_translated(node.local_viewport_rect.origin.x,
|
||||
node.local_viewport_rect.origin.y,
|
||||
0.0);
|
||||
packed_layer.set_transform(transform);
|
||||
|
||||
// Meanwhile, the combined viewport rect is relative to the reference frame, so
|
||||
// we move it into the local coordinate system of the node.
|
||||
let local_viewport_rect =
|
||||
node.combined_local_viewport_rect.translate(&-node.local_viewport_rect.origin.to_vector());
|
||||
node.combined_local_viewport_rect.translate(&-node.local_viewport_rect.origin);
|
||||
|
||||
node_clip_info.screen_bounding_rect = packed_layer.set_rect(&local_viewport_rect,
|
||||
self.screen_rect,
|
||||
|
@ -1605,7 +1593,9 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> {
|
|||
// The world content transform is relative to the containing reference frame,
|
||||
// so we translate into the origin of the stacking context itself.
|
||||
let transform = scroll_node.world_content_transform
|
||||
.pre_translate(stacking_context.reference_frame_offset.to_3d());
|
||||
.pre_translated(stacking_context.reference_frame_offset.x,
|
||||
stacking_context.reference_frame_offset.y,
|
||||
0.0);
|
||||
packed_layer.set_transform(transform);
|
||||
|
||||
if !stacking_context.can_contribute_to_scene() {
|
||||
|
@ -1619,7 +1609,7 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> {
|
|||
.translate(&clip_node.reference_frame_relative_scroll_offset)
|
||||
.translate(&-scroll_node.reference_frame_relative_scroll_offset)
|
||||
.translate(&-stacking_context.reference_frame_offset)
|
||||
.translate(&-scroll_node.scroll_offset());
|
||||
.translate(&-scroll_node.scrolling.offset);
|
||||
group.screen_bounding_rect = packed_layer.set_rect(viewport_rect,
|
||||
self.screen_rect,
|
||||
self.device_pixel_ratio);
|
||||
|
@ -1750,7 +1740,6 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> {
|
|||
self.device_pixel_ratio) {
|
||||
self.frame_builder.prim_store.prepare_prim_for_render(prim_index,
|
||||
self.resource_cache,
|
||||
self.gpu_cache,
|
||||
&packed_layer.transform,
|
||||
self.device_pixel_ratio,
|
||||
display_list);
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct FreeListItemId(u32);
|
||||
|
||||
|
@ -83,6 +85,11 @@ impl<T: FreeListItem> FreeList<T> {
|
|||
&self.items[id.0 as usize]
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, id: FreeListItemId) -> &mut T {
|
||||
debug_assert_eq!(self.free_iter().find(|&fid| fid==id), None);
|
||||
&mut self.items[id.0 as usize]
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.alloc_count
|
||||
|
@ -97,4 +104,20 @@ impl<T: FreeListItem> FreeList<T> {
|
|||
self.first_free_index = Some(id);
|
||||
data
|
||||
}
|
||||
|
||||
pub fn for_each_item<F>(&mut self, f: F) where F: Fn(&mut T) {
|
||||
//TODO: this could be done much faster. Instead of gathering the free
|
||||
// indices into a set, we could re-order the free list to be ascending.
|
||||
// That is an one-time operation with at most O(nf^2), where
|
||||
// nf = number of elements in the free list
|
||||
// Then this code would just walk both `items` and the ascending free
|
||||
// list, essentially skipping the free indices for free.
|
||||
let free_ids: HashSet<_> = self.free_iter().collect();
|
||||
|
||||
for (index, mut item) in self.items.iter_mut().enumerate() {
|
||||
if !free_ids.contains(&FreeListItemId(index as u32)) {
|
||||
f(&mut item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,8 +41,10 @@ pub fn ray_intersects_rect(ray_origin: Point3D<f32>,
|
|||
];
|
||||
|
||||
let parameters = [
|
||||
rect.origin.to_3d(),
|
||||
rect.bottom_right().to_3d(),
|
||||
Point3D::new(rect.origin.x, rect.origin.y, 0.0),
|
||||
Point3D::new(rect.origin.x + rect.size.width,
|
||||
rect.origin.y + rect.size.height,
|
||||
0.0),
|
||||
];
|
||||
|
||||
let mut tmin = (parameters[sign[0]].x - ray_origin.x) * inv_direction.x;
|
||||
|
|
|
@ -15,7 +15,7 @@ use std::sync::mpsc::{channel, Receiver, Sender};
|
|||
use std::collections::HashSet;
|
||||
use std::mem;
|
||||
use texture_cache::{TextureCacheItemId, TextureCache};
|
||||
use webrender_traits::FontTemplate;
|
||||
use internal_types::FontTemplate;
|
||||
use webrender_traits::{FontKey, FontRenderMode, ImageData, ImageFormat};
|
||||
use webrender_traits::{ImageDescriptor, ColorF, LayoutPoint};
|
||||
use webrender_traits::{GlyphKey, GlyphOptions, GlyphInstance, GlyphDimensions};
|
||||
|
@ -248,7 +248,9 @@ impl GlyphRasterizer {
|
|||
for job in rasterized_glyphs {
|
||||
let image_id = job.result.and_then(
|
||||
|glyph| if glyph.width > 0 && glyph.height > 0 {
|
||||
let image_id = texture_cache.insert(
|
||||
let image_id = texture_cache.new_item_id();
|
||||
texture_cache.insert(
|
||||
image_id,
|
||||
ImageDescriptor {
|
||||
width: glyph.width,
|
||||
height: glyph.height,
|
||||
|
|
|
@ -27,22 +27,15 @@
|
|||
use device::FrameId;
|
||||
use profiler::GpuCacheProfileCounters;
|
||||
use renderer::MAX_VERTEX_TEXTURE_WIDTH;
|
||||
use std::{mem, u32};
|
||||
use std::mem;
|
||||
use webrender_traits::{ColorF, LayerRect};
|
||||
|
||||
pub const GPU_CACHE_INITIAL_HEIGHT: u32 = 512;
|
||||
const FRAMES_BEFORE_EVICTION: usize = 10;
|
||||
const NEW_ROWS_PER_RESIZE: u32 = 512;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
struct Epoch(u32);
|
||||
|
||||
impl Epoch {
|
||||
fn next(&mut self) {
|
||||
*self = Epoch(self.0.wrapping_add(1));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct CacheLocation {
|
||||
block_index: BlockIndex,
|
||||
|
@ -200,10 +193,6 @@ struct FreeBlockLists {
|
|||
free_list_2: Option<BlockIndex>,
|
||||
free_list_4: Option<BlockIndex>,
|
||||
free_list_8: Option<BlockIndex>,
|
||||
free_list_16: Option<BlockIndex>,
|
||||
free_list_32: Option<BlockIndex>,
|
||||
free_list_64: Option<BlockIndex>,
|
||||
free_list_128: Option<BlockIndex>,
|
||||
free_list_large: Option<BlockIndex>,
|
||||
}
|
||||
|
||||
|
@ -214,10 +203,6 @@ impl FreeBlockLists {
|
|||
free_list_2: None,
|
||||
free_list_4: None,
|
||||
free_list_8: None,
|
||||
free_list_16: None,
|
||||
free_list_32: None,
|
||||
free_list_64: None,
|
||||
free_list_128: None,
|
||||
free_list_large: None,
|
||||
}
|
||||
}
|
||||
|
@ -232,11 +217,7 @@ impl FreeBlockLists {
|
|||
2 => (2, &mut self.free_list_2),
|
||||
3...4 => (4, &mut self.free_list_4),
|
||||
5...8 => (8, &mut self.free_list_8),
|
||||
9...16 => (16, &mut self.free_list_16),
|
||||
17...32 => (32, &mut self.free_list_32),
|
||||
33...64 => (64, &mut self.free_list_64),
|
||||
65...128 => (128, &mut self.free_list_128),
|
||||
129...MAX_VERTEX_TEXTURE_WIDTH => (MAX_VERTEX_TEXTURE_WIDTH, &mut self.free_list_large),
|
||||
9...MAX_VERTEX_TEXTURE_WIDTH => (MAX_VERTEX_TEXTURE_WIDTH, &mut self.free_list_large),
|
||||
_ => panic!("Can't allocate > MAX_VERTEX_TEXTURE_WIDTH per resource!"),
|
||||
}
|
||||
}
|
||||
|
@ -294,8 +275,10 @@ impl Texture {
|
|||
|
||||
// See if we need a new row (if free-list has nothing available)
|
||||
if free_list.is_none() {
|
||||
// TODO(gw): Handle the case where we need to resize
|
||||
// the cache texture itself!
|
||||
if self.rows.len() as u32 == self.height {
|
||||
self.height += NEW_ROWS_PER_RESIZE;
|
||||
panic!("need to re-alloc texture!!");
|
||||
}
|
||||
|
||||
// Create a new row.
|
||||
|
@ -375,7 +358,7 @@ impl Texture {
|
|||
let (_, free_list) = self.free_lists
|
||||
.get_actual_block_count_and_free_list(row.block_count_per_item);
|
||||
|
||||
block.epoch.next();
|
||||
block.epoch = Epoch(block.epoch.0 + 1);
|
||||
block.next = *free_list;
|
||||
*free_list = Some(index);
|
||||
|
||||
|
@ -461,16 +444,6 @@ impl GpuCache {
|
|||
self.texture.evict_old_blocks(self.frame_id);
|
||||
}
|
||||
|
||||
// Invalidate a (possibly) existing block in the cache.
|
||||
// This means the next call to request() for this location
|
||||
// will rebuild the data and upload it to the GPU.
|
||||
pub fn invalidate(&mut self, handle: &GpuCacheHandle) {
|
||||
if let Some(ref location) = handle.location {
|
||||
let block = &mut self.texture.blocks[location.block_index.0];
|
||||
block.epoch.next();
|
||||
}
|
||||
}
|
||||
|
||||
// Request a resource be added to the cache. If the resource
|
||||
/// is already in the cache, `None` will be returned.
|
||||
pub fn request<'a>(&'a mut self, handle: &'a mut GpuCacheHandle) -> Option<GpuDataRequest<'a>> {
|
||||
|
@ -483,7 +456,6 @@ impl GpuCache {
|
|||
return None
|
||||
}
|
||||
}
|
||||
|
||||
Some(GpuDataRequest {
|
||||
handle: handle,
|
||||
frame_id: self.frame_id,
|
||||
|
@ -492,23 +464,6 @@ impl GpuCache {
|
|||
})
|
||||
}
|
||||
|
||||
// Push an array of data blocks to be uploaded to the GPU
|
||||
// unconditionally for this frame. The cache handle will
|
||||
// assert if the caller tries to retrieve the address
|
||||
// of this handle on a subsequent frame. This is typically
|
||||
// used for uploading data that changes every frame, and
|
||||
// therefore makes no sense to try and cache.
|
||||
pub fn push_per_frame_blocks(&mut self, blocks: &[GpuBlockData]) -> GpuCacheHandle {
|
||||
let start_index = self.texture.pending_blocks.len();
|
||||
self.texture.pending_blocks.extend_from_slice(blocks);
|
||||
let location = self.texture.push_data(start_index,
|
||||
blocks.len(),
|
||||
self.frame_id);
|
||||
GpuCacheHandle {
|
||||
location: Some(location),
|
||||
}
|
||||
}
|
||||
|
||||
/// End the frame. Return the list of updates to apply to the
|
||||
/// device specific cache texture.
|
||||
pub fn end_frame(&mut self,
|
||||
|
|
|
@ -48,11 +48,18 @@ pub enum SourceTexture {
|
|||
}
|
||||
|
||||
const COLOR_FLOAT_TO_FIXED: f32 = 255.0;
|
||||
const COLOR_FLOAT_TO_FIXED_WIDE: f32 = 65535.0;
|
||||
pub const ANGLE_FLOAT_TO_FIXED: f32 = 65535.0;
|
||||
|
||||
pub const ORTHO_NEAR_PLANE: f32 = -1000000.0;
|
||||
pub const ORTHO_FAR_PLANE: f32 = 1000000.0;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum FontTemplate {
|
||||
Raw(Arc<Vec<u8>>, u32),
|
||||
Native(NativeFontHandle),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum TextureSampler {
|
||||
Color0,
|
||||
|
@ -60,11 +67,15 @@ pub enum TextureSampler {
|
|||
Color2,
|
||||
CacheA8,
|
||||
CacheRGBA8,
|
||||
Data16,
|
||||
Data32,
|
||||
ResourceCache,
|
||||
Layers,
|
||||
RenderTasks,
|
||||
Geometry,
|
||||
ResourceRects,
|
||||
Gradients,
|
||||
SplitGeometry,
|
||||
Dither,
|
||||
}
|
||||
|
||||
|
@ -132,6 +143,7 @@ pub enum ClipAttribute {
|
|||
}
|
||||
|
||||
// A packed RGBA8 color ordered for vertex data or similar.
|
||||
// Use PackedTexel instead if intending to upload to a texture.
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
|
@ -153,6 +165,37 @@ impl PackedColor {
|
|||
}
|
||||
}
|
||||
|
||||
// RGBA8 textures currently pack texels in BGRA format for upload.
|
||||
// PackedTexel abstracts away this difference from PackedColor.
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct PackedTexel {
|
||||
pub b: u8,
|
||||
pub g: u8,
|
||||
pub r: u8,
|
||||
pub a: u8,
|
||||
}
|
||||
|
||||
impl PackedTexel {
|
||||
pub fn high_bytes(color: &ColorF) -> PackedTexel {
|
||||
Self::extract_bytes(color, 8)
|
||||
}
|
||||
|
||||
pub fn low_bytes(color: &ColorF) -> PackedTexel {
|
||||
Self::extract_bytes(color, 0)
|
||||
}
|
||||
|
||||
fn extract_bytes(color: &ColorF, shift_by: i32) -> PackedTexel {
|
||||
PackedTexel {
|
||||
b: ((0.5 + color.b * COLOR_FLOAT_TO_FIXED_WIDE).floor() as u32 >> shift_by & 0xff) as u8,
|
||||
g: ((0.5 + color.g * COLOR_FLOAT_TO_FIXED_WIDE).floor() as u32 >> shift_by & 0xff) as u8,
|
||||
r: ((0.5 + color.r * COLOR_FLOAT_TO_FIXED_WIDE).floor() as u32 >> shift_by & 0xff) as u8,
|
||||
a: ((0.5 + color.a * COLOR_FLOAT_TO_FIXED_WIDE).floor() as u32 >> shift_by & 0xff) as u8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct PackedVertex {
|
||||
|
|
|
@ -8,9 +8,9 @@ use border::BorderCornerInstance;
|
|||
use euclid::{Size2D};
|
||||
use gpu_cache::{GpuBlockData, GpuCache, GpuCacheHandle, GpuDataRequest, ToGpuBlocks};
|
||||
use gpu_store::GpuStoreAddress;
|
||||
use internal_types::SourceTexture;
|
||||
use internal_types::{SourceTexture, PackedTexel};
|
||||
use mask_cache::{ClipMode, ClipSource, MaskCacheInfo};
|
||||
use renderer::{VertexDataStore, MAX_VERTEX_TEXTURE_WIDTH};
|
||||
use renderer::{VertexDataStore, GradientDataStore, SplitGeometryStore, MAX_VERTEX_TEXTURE_WIDTH};
|
||||
use render_task::{RenderTask, RenderTaskLocation};
|
||||
use resource_cache::{CacheItem, ImageProperties, ResourceCache};
|
||||
use std::mem;
|
||||
|
@ -23,7 +23,7 @@ use webrender_traits::{device_length, DeviceIntRect, DeviceIntSize};
|
|||
use webrender_traits::{DeviceRect, DevicePoint, DeviceSize};
|
||||
use webrender_traits::{LayerRect, LayerSize, LayerPoint, LayoutPoint};
|
||||
use webrender_traits::{LayerToWorldTransform, GlyphInstance, GlyphOptions};
|
||||
use webrender_traits::{ExtendMode, GradientStop, TileOffset};
|
||||
use webrender_traits::{ExtendMode, GradientStop, AuxIter, TileOffset};
|
||||
|
||||
pub const CLIP_DATA_GPU_SIZE: usize = 5;
|
||||
pub const MASK_DATA_GPU_SIZE: usize = 1;
|
||||
|
@ -89,6 +89,22 @@ pub enum PrimitiveKind {
|
|||
BoxShadow,
|
||||
}
|
||||
|
||||
/// Geometry description for simple rectangular primitives, uploaded to the GPU.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PrimitiveGeometry {
|
||||
pub local_rect: LayerRect,
|
||||
pub local_clip_rect: LayerRect,
|
||||
}
|
||||
|
||||
impl Default for PrimitiveGeometry {
|
||||
fn default() -> PrimitiveGeometry {
|
||||
PrimitiveGeometry {
|
||||
local_rect: unsafe { mem::uninitialized() },
|
||||
local_clip_rect: unsafe { mem::uninitialized() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum PrimitiveCacheKey {
|
||||
BoxShadow(BoxShadowPrimitiveCacheKey),
|
||||
|
@ -125,12 +141,6 @@ pub struct PrimitiveMetadata {
|
|||
// text run.
|
||||
pub render_task: Option<RenderTask>,
|
||||
pub clip_task: Option<RenderTask>,
|
||||
|
||||
// TODO(gw): In the future, we should just pull these
|
||||
// directly from the DL item, instead of
|
||||
// storing them here.
|
||||
pub local_rect: LayerRect,
|
||||
pub local_clip_rect: LayerRect,
|
||||
}
|
||||
|
||||
impl PrimitiveMetadata {
|
||||
|
@ -139,6 +149,20 @@ impl PrimitiveMetadata {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SplitGeometry {
|
||||
pub data: [f32; 12],
|
||||
}
|
||||
|
||||
impl Default for SplitGeometry {
|
||||
fn default() -> SplitGeometry {
|
||||
SplitGeometry {
|
||||
data: unsafe { mem::uninitialized() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct RectanglePrimitive {
|
||||
|
@ -246,36 +270,45 @@ impl ToGpuBlocks for BoxShadowPrimitiveCpu {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct GradientStopGpu {
|
||||
color: ColorF,
|
||||
offset: f32,
|
||||
padding: [f32; 3],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GradientPrimitiveCpu {
|
||||
pub stops_range: ItemRange<GradientStop>,
|
||||
pub stops_count: usize,
|
||||
pub extend_mode: ExtendMode,
|
||||
pub reverse_stops: bool,
|
||||
pub cache_dirty: bool,
|
||||
pub gpu_data_address: GpuStoreAddress,
|
||||
pub gpu_data_count: i32,
|
||||
pub gpu_blocks: [GpuBlockData; 3],
|
||||
}
|
||||
|
||||
impl GradientPrimitiveCpu {
|
||||
fn build_gpu_blocks_for_aligned(&self,
|
||||
display_list: &BuiltDisplayList,
|
||||
mut request: GpuDataRequest) {
|
||||
impl ToGpuBlocks for GradientPrimitiveCpu {
|
||||
fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
|
||||
request.extend_from_slice(&self.gpu_blocks);
|
||||
let src_stops = display_list.get(self.stops_range);
|
||||
|
||||
for src in src_stops {
|
||||
request.push(src.color.premultiplied().into());
|
||||
request.push([src.offset, 0.0, 0.0, 0.0].into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_gpu_blocks_for_angle_radial(&self,
|
||||
display_list: &BuiltDisplayList,
|
||||
mut request: GpuDataRequest) {
|
||||
#[derive(Debug)]
|
||||
pub struct RadialGradientPrimitiveCpu {
|
||||
pub stops_range: ItemRange<GradientStop>,
|
||||
pub extend_mode: ExtendMode,
|
||||
pub cache_dirty: bool,
|
||||
pub gpu_data_address: GpuStoreAddress,
|
||||
pub gpu_data_count: i32,
|
||||
pub gpu_blocks: [GpuBlockData; 3],
|
||||
}
|
||||
|
||||
impl ToGpuBlocks for RadialGradientPrimitiveCpu {
|
||||
fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
|
||||
request.extend_from_slice(&self.gpu_blocks);
|
||||
|
||||
let gradient_builder = GradientGpuBlockBuilder::new(self.stops_range,
|
||||
display_list);
|
||||
gradient_builder.build(self.reverse_stops, &mut request);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -298,32 +331,47 @@ pub const GRADIENT_DATA_SIZE: usize = GRADIENT_DATA_TABLE_SIZE + 2;
|
|||
#[repr(C)]
|
||||
// An entry in a gradient data table representing a segment of the gradient color space.
|
||||
pub struct GradientDataEntry {
|
||||
pub start_color: ColorF,
|
||||
pub end_color: ColorF,
|
||||
pub start_color: PackedTexel,
|
||||
pub end_color: PackedTexel,
|
||||
}
|
||||
|
||||
struct GradientGpuBlockBuilder<'a> {
|
||||
stops_range: ItemRange<GradientStop>,
|
||||
display_list: &'a BuiltDisplayList,
|
||||
#[repr(C)]
|
||||
// A table of gradient entries, with two colors per entry, that specify the start and end color
|
||||
// within the segment of the gradient space represented by that entry. To lookup a gradient result,
|
||||
// first the entry index is calculated to determine which two colors to interpolate between, then
|
||||
// the offset within that entry bucket is used to interpolate between the two colors in that entry.
|
||||
// This layout preserves hard stops, as the end color for a given entry can differ from the start
|
||||
// color for the following entry, despite them being adjacent. Colors are stored within in BGRA8
|
||||
// format for texture upload. This table requires the gradient color stops to be normalized to the
|
||||
// range [0, 1]. The first and last entries hold the first and last color stop colors respectively,
|
||||
// while the entries in between hold the interpolated color stop values for the range [0, 1].
|
||||
pub struct GradientData {
|
||||
pub colors_high: [GradientDataEntry; GRADIENT_DATA_SIZE],
|
||||
pub colors_low: [GradientDataEntry; GRADIENT_DATA_SIZE],
|
||||
}
|
||||
|
||||
impl<'a> GradientGpuBlockBuilder<'a> {
|
||||
fn new(stops_range: ItemRange<GradientStop>,
|
||||
display_list: &'a BuiltDisplayList) -> GradientGpuBlockBuilder<'a> {
|
||||
GradientGpuBlockBuilder {
|
||||
stops_range: stops_range,
|
||||
display_list: display_list,
|
||||
impl Default for GradientData {
|
||||
fn default() -> GradientData {
|
||||
GradientData {
|
||||
colors_high: unsafe { mem::uninitialized() },
|
||||
colors_low: unsafe { mem::uninitialized() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for GradientData {
|
||||
fn clone(&self) -> GradientData {
|
||||
GradientData {
|
||||
colors_high: self.colors_high,
|
||||
colors_low: self.colors_low,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GradientData {
|
||||
/// Generate a color ramp filling the indices in [start_idx, end_idx) and interpolating
|
||||
/// from start_color to end_color.
|
||||
fn fill_colors(&self,
|
||||
start_idx: usize,
|
||||
end_idx: usize,
|
||||
start_color: &ColorF,
|
||||
end_color: &ColorF,
|
||||
entries: &mut [GradientDataEntry; GRADIENT_DATA_SIZE]) {
|
||||
fn fill_colors(&mut self, start_idx: usize, end_idx: usize, start_color: &ColorF, end_color: &ColorF) {
|
||||
// Calculate the color difference for individual steps in the ramp.
|
||||
let inv_steps = 1.0 / (end_idx - start_idx) as f32;
|
||||
let step_r = (end_color.r - start_color.r) * inv_steps;
|
||||
|
@ -332,16 +380,24 @@ impl<'a> GradientGpuBlockBuilder<'a> {
|
|||
let step_a = (end_color.a - start_color.a) * inv_steps;
|
||||
|
||||
let mut cur_color = *start_color;
|
||||
let mut cur_color_high = PackedTexel::high_bytes(&cur_color);
|
||||
let mut cur_color_low = PackedTexel::low_bytes(&cur_color);
|
||||
|
||||
// Walk the ramp writing start and end colors for each entry.
|
||||
for index in start_idx..end_idx {
|
||||
let entry = &mut entries[index];
|
||||
entry.start_color = cur_color;
|
||||
let high_byte_entry = &mut self.colors_high[index];
|
||||
let low_byte_entry = &mut self.colors_low[index];
|
||||
|
||||
high_byte_entry.start_color = cur_color_high;
|
||||
low_byte_entry.start_color = cur_color_low;
|
||||
cur_color.r += step_r;
|
||||
cur_color.g += step_g;
|
||||
cur_color.b += step_b;
|
||||
cur_color.a += step_a;
|
||||
entry.end_color = cur_color;
|
||||
cur_color_high = PackedTexel::high_bytes(&cur_color);
|
||||
cur_color_low = PackedTexel::low_bytes(&cur_color);
|
||||
high_byte_entry.end_color = cur_color_high;
|
||||
low_byte_entry.end_color = cur_color_low;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -355,8 +411,7 @@ impl<'a> GradientGpuBlockBuilder<'a> {
|
|||
}
|
||||
|
||||
// Build the gradient data from the supplied stops, reversing them if necessary.
|
||||
fn build(&self, reverse_stops: bool, request: &mut GpuDataRequest) {
|
||||
let src_stops = self.display_list.get(self.stops_range);
|
||||
fn build(&mut self, src_stops: AuxIter<GradientStop>, reverse_stops: bool) {
|
||||
|
||||
// Preconditions (should be ensured by DisplayListBuilder):
|
||||
// * we have at least two stops
|
||||
|
@ -368,20 +423,9 @@ impl<'a> GradientGpuBlockBuilder<'a> {
|
|||
let mut cur_color = first.color.premultiplied();
|
||||
debug_assert_eq!(first.offset, 0.0);
|
||||
|
||||
// A table of gradient entries, with two colors per entry, that specify the start and end color
|
||||
// within the segment of the gradient space represented by that entry. To lookup a gradient result,
|
||||
// first the entry index is calculated to determine which two colors to interpolate between, then
|
||||
// the offset within that entry bucket is used to interpolate between the two colors in that entry.
|
||||
// This layout preserves hard stops, as the end color for a given entry can differ from the start
|
||||
// color for the following entry, despite them being adjacent. Colors are stored within in BGRA8
|
||||
// format for texture upload. This table requires the gradient color stops to be normalized to the
|
||||
// range [0, 1]. The first and last entries hold the first and last color stop colors respectively,
|
||||
// while the entries in between hold the interpolated color stop values for the range [0, 1].
|
||||
let mut entries: [GradientDataEntry; GRADIENT_DATA_SIZE] = unsafe { mem::uninitialized() };
|
||||
|
||||
if reverse_stops {
|
||||
// Fill in the first entry (for reversed stops) with the first color stop
|
||||
self.fill_colors(GRADIENT_DATA_LAST_STOP, GRADIENT_DATA_LAST_STOP + 1, &cur_color, &cur_color, &mut entries);
|
||||
self.fill_colors(GRADIENT_DATA_LAST_STOP, GRADIENT_DATA_LAST_STOP + 1, &cur_color, &cur_color);
|
||||
|
||||
// Fill in the center of the gradient table, generating a color ramp between each consecutive pair
|
||||
// of gradient stops. Each iteration of a loop will fill the indices in [next_idx, cur_idx). The
|
||||
|
@ -393,7 +437,7 @@ impl<'a> GradientGpuBlockBuilder<'a> {
|
|||
|
||||
if next_idx < cur_idx {
|
||||
self.fill_colors(next_idx, cur_idx,
|
||||
&next_color, &cur_color, &mut entries);
|
||||
&next_color, &cur_color);
|
||||
cur_idx = next_idx;
|
||||
}
|
||||
|
||||
|
@ -402,10 +446,10 @@ impl<'a> GradientGpuBlockBuilder<'a> {
|
|||
debug_assert_eq!(cur_idx, GRADIENT_DATA_TABLE_BEGIN);
|
||||
|
||||
// Fill in the last entry (for reversed stops) with the last color stop
|
||||
self.fill_colors(GRADIENT_DATA_FIRST_STOP, GRADIENT_DATA_FIRST_STOP + 1, &cur_color, &cur_color, &mut entries);
|
||||
self.fill_colors(GRADIENT_DATA_FIRST_STOP, GRADIENT_DATA_FIRST_STOP + 1, &cur_color, &cur_color);
|
||||
} else {
|
||||
// Fill in the first entry with the first color stop
|
||||
self.fill_colors(GRADIENT_DATA_FIRST_STOP, GRADIENT_DATA_FIRST_STOP + 1, &cur_color, &cur_color, &mut entries);
|
||||
self.fill_colors(GRADIENT_DATA_FIRST_STOP, GRADIENT_DATA_FIRST_STOP + 1, &cur_color, &cur_color);
|
||||
|
||||
// Fill in the center of the gradient table, generating a color ramp between each consecutive pair
|
||||
// of gradient stops. Each iteration of a loop will fill the indices in [cur_idx, next_idx). The
|
||||
|
@ -417,7 +461,7 @@ impl<'a> GradientGpuBlockBuilder<'a> {
|
|||
|
||||
if next_idx > cur_idx {
|
||||
self.fill_colors(cur_idx, next_idx,
|
||||
&cur_color, &next_color, &mut entries);
|
||||
&cur_color, &next_color);
|
||||
cur_idx = next_idx;
|
||||
}
|
||||
|
||||
|
@ -426,35 +470,15 @@ impl<'a> GradientGpuBlockBuilder<'a> {
|
|||
debug_assert_eq!(cur_idx, GRADIENT_DATA_TABLE_END);
|
||||
|
||||
// Fill in the last entry with the last color stop
|
||||
self.fill_colors(GRADIENT_DATA_LAST_STOP, GRADIENT_DATA_LAST_STOP + 1, &cur_color, &cur_color, &mut entries);
|
||||
}
|
||||
|
||||
for entry in entries.iter() {
|
||||
request.push(entry.start_color.into());
|
||||
request.push(entry.end_color.into());
|
||||
self.fill_colors(GRADIENT_DATA_LAST_STOP, GRADIENT_DATA_LAST_STOP + 1, &cur_color, &cur_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RadialGradientPrimitiveCpu {
|
||||
pub stops_range: ItemRange<GradientStop>,
|
||||
pub extend_mode: ExtendMode,
|
||||
pub gpu_data_address: GpuStoreAddress,
|
||||
pub gpu_data_count: i32,
|
||||
pub gpu_blocks: [GpuBlockData; 3],
|
||||
}
|
||||
|
||||
impl RadialGradientPrimitiveCpu {
|
||||
fn build_gpu_blocks_for_angle_radial(&self,
|
||||
display_list: &BuiltDisplayList,
|
||||
mut request: GpuDataRequest) {
|
||||
request.extend_from_slice(&self.gpu_blocks);
|
||||
|
||||
let gradient_builder = GradientGpuBlockBuilder::new(self.stops_range,
|
||||
display_list);
|
||||
gradient_builder.build(false, &mut request);
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(C)]
|
||||
struct InstanceRect {
|
||||
rect: LayerRect,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -464,6 +488,7 @@ pub struct TextRunPrimitiveCpu {
|
|||
pub blur_radius: f32,
|
||||
pub glyph_range: ItemRange<GlyphInstance>,
|
||||
pub glyph_count: usize,
|
||||
pub cache_dirty: bool,
|
||||
// TODO(gw): Maybe make this an Arc for sharing with resource cache
|
||||
pub glyph_instances: Vec<GlyphInstance>,
|
||||
pub color_texture_id: SourceTexture,
|
||||
|
@ -471,24 +496,13 @@ pub struct TextRunPrimitiveCpu {
|
|||
pub render_mode: FontRenderMode,
|
||||
pub resource_address: GpuStoreAddress,
|
||||
pub glyph_options: Option<GlyphOptions>,
|
||||
pub gpu_data_address: GpuStoreAddress,
|
||||
pub gpu_data_count: i32,
|
||||
}
|
||||
|
||||
impl ToGpuBlocks for TextRunPrimitiveCpu {
|
||||
fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
|
||||
request.push(self.color.into());
|
||||
|
||||
// Two glyphs are packed per GPU block.
|
||||
for glyph_chunk in self.glyph_instances.chunks(2) {
|
||||
// In the case of an odd number of glyphs, the
|
||||
// last glyph will get duplicated in the final
|
||||
// GPU block.
|
||||
let first_glyph = glyph_chunk.first().unwrap();
|
||||
let second_glyph = glyph_chunk.last().unwrap();
|
||||
request.push([first_glyph.point.x,
|
||||
first_glyph.point.y,
|
||||
second_glyph.point.x,
|
||||
second_glyph.point.y].into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -652,7 +666,13 @@ pub struct PrimitiveStore {
|
|||
pub cpu_box_shadows: Vec<BoxShadowPrimitiveCpu>,
|
||||
|
||||
/// Gets uploaded directly to GPU via vertex texture.
|
||||
pub gpu_geometry: VertexDataStore<PrimitiveGeometry>,
|
||||
pub gpu_data16: VertexDataStore<GpuBlock16>,
|
||||
pub gpu_data32: VertexDataStore<GpuBlock32>,
|
||||
pub gpu_gradient_data: GradientDataStore,
|
||||
|
||||
/// Geometry generated by plane splitting.
|
||||
pub gpu_split_geometry: SplitGeometryStore,
|
||||
|
||||
/// Resolved resource rects.
|
||||
pub gpu_resource_rects: VertexDataStore<TexelRect>,
|
||||
|
@ -675,7 +695,11 @@ impl PrimitiveStore {
|
|||
cpu_borders: Vec::new(),
|
||||
cpu_box_shadows: Vec::new(),
|
||||
prims_to_resolve: Vec::new(),
|
||||
gpu_geometry: VertexDataStore::new(),
|
||||
gpu_data16: VertexDataStore::new(),
|
||||
gpu_data32: VertexDataStore::new(),
|
||||
gpu_gradient_data: GradientDataStore::new(),
|
||||
gpu_split_geometry: SplitGeometryStore::new(),
|
||||
gpu_resource_rects: VertexDataStore::new(),
|
||||
}
|
||||
}
|
||||
|
@ -693,7 +717,11 @@ impl PrimitiveStore {
|
|||
cpu_borders: recycle_vec(self.cpu_borders),
|
||||
cpu_box_shadows: recycle_vec(self.cpu_box_shadows),
|
||||
prims_to_resolve: recycle_vec(self.prims_to_resolve),
|
||||
gpu_geometry: self.gpu_geometry.recycle(),
|
||||
gpu_data16: self.gpu_data16.recycle(),
|
||||
gpu_data32: self.gpu_data32.recycle(),
|
||||
gpu_gradient_data: self.gpu_gradient_data.recycle(),
|
||||
gpu_split_geometry: self.gpu_split_geometry.recycle(),
|
||||
gpu_resource_rects: self.gpu_resource_rects.recycle(),
|
||||
}
|
||||
}
|
||||
|
@ -707,13 +735,13 @@ impl PrimitiveStore {
|
|||
}
|
||||
|
||||
pub fn add_primitive(&mut self,
|
||||
local_rect: &LayerRect,
|
||||
local_clip_rect: &LayerRect,
|
||||
geometry: PrimitiveGeometry,
|
||||
clips: Vec<ClipSource>,
|
||||
clip_info: Option<MaskCacheInfo>,
|
||||
container: PrimitiveContainer) -> PrimitiveIndex {
|
||||
let prim_index = self.cpu_metadata.len();
|
||||
self.cpu_bounding_rects.push(None);
|
||||
self.gpu_geometry.push(geometry);
|
||||
|
||||
let metadata = match container {
|
||||
PrimitiveContainer::Rectangle(rect) => {
|
||||
|
@ -728,8 +756,6 @@ impl PrimitiveStore {
|
|||
gpu_location: GpuCacheHandle::new(),
|
||||
render_task: None,
|
||||
clip_task: None,
|
||||
local_rect: *local_rect,
|
||||
local_clip_rect: *local_clip_rect,
|
||||
};
|
||||
|
||||
self.cpu_rectangles.push(rect);
|
||||
|
@ -737,7 +763,10 @@ impl PrimitiveStore {
|
|||
metadata
|
||||
}
|
||||
PrimitiveContainer::TextRun(mut text_cpu) => {
|
||||
let gpu_glyphs_address = self.gpu_data16.alloc(text_cpu.glyph_count);
|
||||
text_cpu.resource_address = self.gpu_resource_rects.alloc(text_cpu.glyph_count);
|
||||
text_cpu.gpu_data_address = gpu_glyphs_address;
|
||||
text_cpu.gpu_data_count = text_cpu.glyph_count as i32;
|
||||
|
||||
let metadata = PrimitiveMetadata {
|
||||
is_opaque: false,
|
||||
|
@ -748,8 +777,6 @@ impl PrimitiveStore {
|
|||
gpu_location: GpuCacheHandle::new(),
|
||||
render_task: None,
|
||||
clip_task: None,
|
||||
local_rect: *local_rect,
|
||||
local_clip_rect: *local_clip_rect,
|
||||
};
|
||||
|
||||
self.cpu_text_runs.push(text_cpu);
|
||||
|
@ -767,8 +794,6 @@ impl PrimitiveStore {
|
|||
gpu_location: GpuCacheHandle::new(),
|
||||
render_task: None,
|
||||
clip_task: None,
|
||||
local_rect: *local_rect,
|
||||
local_clip_rect: *local_clip_rect,
|
||||
};
|
||||
|
||||
self.cpu_images.push(image_cpu);
|
||||
|
@ -786,8 +811,6 @@ impl PrimitiveStore {
|
|||
gpu_location: GpuCacheHandle::new(),
|
||||
render_task: None,
|
||||
clip_task: None,
|
||||
local_rect: *local_rect,
|
||||
local_clip_rect: *local_clip_rect,
|
||||
};
|
||||
|
||||
self.cpu_yuv_images.push(image_cpu);
|
||||
|
@ -803,14 +826,17 @@ impl PrimitiveStore {
|
|||
gpu_location: GpuCacheHandle::new(),
|
||||
render_task: None,
|
||||
clip_task: None,
|
||||
local_rect: *local_rect,
|
||||
local_clip_rect: *local_clip_rect,
|
||||
};
|
||||
|
||||
self.cpu_borders.push(border_cpu);
|
||||
metadata
|
||||
}
|
||||
PrimitiveContainer::AlignedGradient(gradient_cpu) => {
|
||||
PrimitiveContainer::AlignedGradient(mut gradient_cpu) => {
|
||||
let gpu_stops_address = self.gpu_data32.alloc(gradient_cpu.stops_count);
|
||||
|
||||
gradient_cpu.gpu_data_address = gpu_stops_address;
|
||||
gradient_cpu.gpu_data_count = gradient_cpu.stops_count as i32;
|
||||
|
||||
let metadata = PrimitiveMetadata {
|
||||
// TODO: calculate if the gradient is actually opaque
|
||||
is_opaque: false,
|
||||
|
@ -821,14 +847,17 @@ impl PrimitiveStore {
|
|||
gpu_location: GpuCacheHandle::new(),
|
||||
render_task: None,
|
||||
clip_task: None,
|
||||
local_rect: *local_rect,
|
||||
local_clip_rect: *local_clip_rect,
|
||||
};
|
||||
|
||||
self.cpu_gradients.push(gradient_cpu);
|
||||
metadata
|
||||
}
|
||||
PrimitiveContainer::AngleGradient(gradient_cpu) => {
|
||||
PrimitiveContainer::AngleGradient(mut gradient_cpu) => {
|
||||
let gpu_gradient_address = self.gpu_gradient_data.alloc(1);
|
||||
|
||||
gradient_cpu.gpu_data_address = gpu_gradient_address;
|
||||
gradient_cpu.gpu_data_count = 1;
|
||||
|
||||
let metadata = PrimitiveMetadata {
|
||||
// TODO: calculate if the gradient is actually opaque
|
||||
is_opaque: false,
|
||||
|
@ -839,14 +868,17 @@ impl PrimitiveStore {
|
|||
gpu_location: GpuCacheHandle::new(),
|
||||
render_task: None,
|
||||
clip_task: None,
|
||||
local_rect: *local_rect,
|
||||
local_clip_rect: *local_clip_rect,
|
||||
};
|
||||
|
||||
self.cpu_gradients.push(gradient_cpu);
|
||||
metadata
|
||||
}
|
||||
PrimitiveContainer::RadialGradient(radial_gradient_cpu) => {
|
||||
PrimitiveContainer::RadialGradient(mut radial_gradient_cpu) => {
|
||||
let gpu_gradient_address = self.gpu_gradient_data.alloc(1);
|
||||
|
||||
radial_gradient_cpu.gpu_data_address = gpu_gradient_address;
|
||||
radial_gradient_cpu.gpu_data_count = 1;
|
||||
|
||||
let metadata = PrimitiveMetadata {
|
||||
// TODO: calculate if the gradient is actually opaque
|
||||
is_opaque: false,
|
||||
|
@ -857,8 +889,6 @@ impl PrimitiveStore {
|
|||
gpu_location: GpuCacheHandle::new(),
|
||||
render_task: None,
|
||||
clip_task: None,
|
||||
local_rect: *local_rect,
|
||||
local_clip_rect: *local_clip_rect,
|
||||
};
|
||||
|
||||
self.cpu_radial_gradients.push(radial_gradient_cpu);
|
||||
|
@ -899,8 +929,6 @@ impl PrimitiveStore {
|
|||
gpu_location: GpuCacheHandle::new(),
|
||||
render_task: Some(render_task),
|
||||
clip_task: None,
|
||||
local_rect: *local_rect,
|
||||
local_clip_rect: *local_clip_rect,
|
||||
};
|
||||
|
||||
self.cpu_box_shadows.push(box_shadow);
|
||||
|
@ -1074,6 +1102,28 @@ impl PrimitiveStore {
|
|||
deferred_resolves
|
||||
}
|
||||
|
||||
pub fn set_clip_source(&mut self, index: PrimitiveIndex, source: Option<ClipSource>) {
|
||||
let metadata = &mut self.cpu_metadata[index.0];
|
||||
metadata.clips = match source {
|
||||
Some(source) => {
|
||||
let (rect, is_complex) = match source {
|
||||
ClipSource::Complex(rect, radius, _) => (rect, radius > 0.0),
|
||||
ClipSource::Region(ref region, _) => (region.main, region.is_complex()),
|
||||
ClipSource::BorderCorner{..} => panic!("Not supported!"),
|
||||
};
|
||||
self.gpu_geometry.get_mut(GpuStoreAddress(index.0 as i32))
|
||||
.local_clip_rect = rect;
|
||||
if is_complex {
|
||||
metadata.clip_cache_info = None; //CLIP TODO: re-use the existing GPU allocation
|
||||
}
|
||||
vec![source]
|
||||
}
|
||||
None => {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_metadata(&self, index: PrimitiveIndex) -> &PrimitiveMetadata {
|
||||
&self.cpu_metadata[index.0]
|
||||
}
|
||||
|
@ -1088,12 +1138,12 @@ impl PrimitiveStore {
|
|||
layer_transform: &LayerToWorldTransform,
|
||||
layer_combined_local_clip_rect: &LayerRect,
|
||||
device_pixel_ratio: f32) -> bool {
|
||||
let metadata = &self.cpu_metadata[prim_index.0];
|
||||
let geom = &self.gpu_geometry.get(GpuStoreAddress(prim_index.0 as i32));
|
||||
|
||||
let bounding_rect = metadata.local_rect
|
||||
.intersection(&metadata.local_clip_rect)
|
||||
.and_then(|rect| rect.intersection(layer_combined_local_clip_rect))
|
||||
.and_then(|ref local_rect| {
|
||||
let bounding_rect = geom.local_rect
|
||||
.intersection(&geom.local_clip_rect)
|
||||
.and_then(|rect| rect.intersection(layer_combined_local_clip_rect))
|
||||
.and_then(|ref local_rect| {
|
||||
let xf_rect = TransformedRect::new(local_rect,
|
||||
layer_transform,
|
||||
device_pixel_ratio);
|
||||
|
@ -1108,7 +1158,6 @@ impl PrimitiveStore {
|
|||
pub fn prepare_prim_for_render(&mut self,
|
||||
prim_index: PrimitiveIndex,
|
||||
resource_cache: &mut ResourceCache,
|
||||
gpu_cache: &mut GpuCache,
|
||||
layer_transform: &LayerToWorldTransform,
|
||||
device_pixel_ratio: f32,
|
||||
display_list: &BuiltDisplayList) {
|
||||
|
@ -1116,6 +1165,45 @@ impl PrimitiveStore {
|
|||
let metadata = &mut self.cpu_metadata[prim_index.0];
|
||||
let mut prim_needs_resolve = false;
|
||||
|
||||
// Mark this GPU resource as required for this frame.
|
||||
if let Some(request) = resource_cache.gpu_cache.request(&mut metadata.gpu_location) {
|
||||
match metadata.prim_kind {
|
||||
PrimitiveKind::Rectangle => {
|
||||
let rect = &self.cpu_rectangles[metadata.cpu_prim_index.0];
|
||||
rect.write_gpu_blocks(request);
|
||||
}
|
||||
PrimitiveKind::Border => {
|
||||
let border = &self.cpu_borders[metadata.cpu_prim_index.0];
|
||||
border.write_gpu_blocks(request);
|
||||
}
|
||||
PrimitiveKind::BoxShadow => {
|
||||
let box_shadow = &self.cpu_box_shadows[metadata.cpu_prim_index.0];
|
||||
box_shadow.write_gpu_blocks(request);
|
||||
}
|
||||
PrimitiveKind::Image => {
|
||||
let image = &self.cpu_images[metadata.cpu_prim_index.0];
|
||||
image.write_gpu_blocks(request);
|
||||
}
|
||||
PrimitiveKind::YuvImage => {
|
||||
let yuv_image = &self.cpu_yuv_images[metadata.cpu_prim_index.0];
|
||||
yuv_image.write_gpu_blocks(request);
|
||||
}
|
||||
PrimitiveKind::AlignedGradient |
|
||||
PrimitiveKind::AngleGradient => {
|
||||
let gradient = &self.cpu_gradients[metadata.cpu_prim_index.0];
|
||||
gradient.write_gpu_blocks(request);
|
||||
}
|
||||
PrimitiveKind::RadialGradient => {
|
||||
let gradient = &self.cpu_radial_gradients[metadata.cpu_prim_index.0];
|
||||
gradient.write_gpu_blocks(request);
|
||||
}
|
||||
PrimitiveKind::TextRun => {
|
||||
let text = &self.cpu_text_runs[metadata.cpu_prim_index.0];
|
||||
text.write_gpu_blocks(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref mut clip_info) = metadata.clip_cache_info {
|
||||
clip_info.update(&metadata.clips,
|
||||
layer_transform,
|
||||
|
@ -1155,14 +1243,23 @@ impl PrimitiveStore {
|
|||
let src_glyphs = display_list.get(text.glyph_range);
|
||||
prim_needs_resolve = true;
|
||||
|
||||
// Cache the glyph positions, if not in the cache already.
|
||||
if text.glyph_instances.is_empty() {
|
||||
if text.cache_dirty {
|
||||
text.cache_dirty = false;
|
||||
|
||||
debug_assert!(text.gpu_data_count == src_glyphs.len() as i32);
|
||||
debug_assert!(text.glyph_instances.is_empty());
|
||||
|
||||
let dest_glyphs = self.gpu_data16.get_slice_mut(text.gpu_data_address,
|
||||
src_glyphs.len());
|
||||
|
||||
let mut glyph_key = GlyphKey::new(text.font_key,
|
||||
font_size_dp,
|
||||
text.color,
|
||||
0,
|
||||
LayoutPoint::new(0.0, 0.0),
|
||||
text.render_mode);
|
||||
let mut actual_glyph_count = 0;
|
||||
|
||||
for src in src_glyphs {
|
||||
glyph_key.index = src.index;
|
||||
glyph_key.subpixel_point.set_offset(src.point, text.render_mode);
|
||||
|
@ -1180,31 +1277,42 @@ impl PrimitiveStore {
|
|||
|
||||
let glyph_pos = LayerPoint::new(x, y);
|
||||
|
||||
dest_glyphs[actual_glyph_count] = GpuBlock16::from(GlyphPrimitive {
|
||||
padding: LayerPoint::zero(),
|
||||
offset: glyph_pos,
|
||||
});
|
||||
|
||||
text.glyph_instances.push(GlyphInstance {
|
||||
index: src.index,
|
||||
point: glyph_pos,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
metadata.render_task = if text.blur_radius == 0.0 {
|
||||
None
|
||||
} else {
|
||||
// This is a text-shadow element. Create a render task that will
|
||||
// render the text run to a target, and then apply a gaussian
|
||||
// blur to that text run in order to build the actual primitive
|
||||
// which will be blitted to the framebuffer.
|
||||
let cache_width = (metadata.local_rect.size.width * device_pixel_ratio).ceil() as i32;
|
||||
let cache_height = (metadata.local_rect.size.height * device_pixel_ratio).ceil() as i32;
|
||||
let cache_size = DeviceIntSize::new(cache_width, cache_height);
|
||||
let cache_key = PrimitiveCacheKey::TextShadow(prim_index);
|
||||
let blur_radius = device_length(text.blur_radius,
|
||||
device_pixel_ratio);
|
||||
Some(RenderTask::new_blur(cache_key,
|
||||
cache_size,
|
||||
blur_radius,
|
||||
prim_index))
|
||||
};
|
||||
actual_glyph_count += 1;
|
||||
}
|
||||
|
||||
let render_task = if text.blur_radius == 0.0 {
|
||||
None
|
||||
} else {
|
||||
// This is a text-shadow element. Create a render task that will
|
||||
// render the text run to a target, and then apply a gaussian
|
||||
// blur to that text run in order to build the actual primitive
|
||||
// which will be blitted to the framebuffer.
|
||||
let geom = &self.gpu_geometry.get(GpuStoreAddress(prim_index.0 as i32));
|
||||
let cache_width = (geom.local_rect.size.width * device_pixel_ratio).ceil() as i32;
|
||||
let cache_height = (geom.local_rect.size.height * device_pixel_ratio).ceil() as i32;
|
||||
let cache_size = DeviceIntSize::new(cache_width, cache_height);
|
||||
let cache_key = PrimitiveCacheKey::TextShadow(prim_index);
|
||||
let blur_radius = device_length(text.blur_radius,
|
||||
device_pixel_ratio);
|
||||
Some(RenderTask::new_blur(cache_key,
|
||||
cache_size,
|
||||
blur_radius,
|
||||
prim_index))
|
||||
};
|
||||
|
||||
text.gpu_data_count = actual_glyph_count as i32;
|
||||
metadata.render_task = render_task;
|
||||
}
|
||||
|
||||
resource_cache.request_glyphs(text.font_key,
|
||||
font_size_dp,
|
||||
|
@ -1246,55 +1354,44 @@ impl PrimitiveStore {
|
|||
// TODO(nical): Currently assuming no tile_spacing for yuv images.
|
||||
metadata.is_opaque = true;
|
||||
}
|
||||
PrimitiveKind::AlignedGradient |
|
||||
PrimitiveKind::AngleGradient |
|
||||
PrimitiveKind::RadialGradient => {}
|
||||
}
|
||||
PrimitiveKind::AlignedGradient => {
|
||||
let gradient = &mut self.cpu_gradients[metadata.cpu_prim_index.0];
|
||||
if gradient.cache_dirty {
|
||||
let src_stops = display_list.get(gradient.stops_range);
|
||||
|
||||
// Mark this GPU resource as required for this frame.
|
||||
if let Some(mut request) = gpu_cache.request(&mut metadata.gpu_location) {
|
||||
request.push(metadata.local_rect.into());
|
||||
request.push(metadata.local_clip_rect.into());
|
||||
debug_assert!(gradient.gpu_data_count == src_stops.len() as i32);
|
||||
let dest_stops = self.gpu_data32.get_slice_mut(gradient.gpu_data_address,
|
||||
src_stops.len());
|
||||
|
||||
match metadata.prim_kind {
|
||||
PrimitiveKind::Rectangle => {
|
||||
let rect = &self.cpu_rectangles[metadata.cpu_prim_index.0];
|
||||
rect.write_gpu_blocks(request);
|
||||
for (src, dest) in src_stops.zip(dest_stops.iter_mut()) {
|
||||
*dest = GpuBlock32::from(GradientStopGpu {
|
||||
offset: src.offset,
|
||||
color: src.color.premultiplied(),
|
||||
padding: [0.0; 3],
|
||||
});
|
||||
}
|
||||
|
||||
gradient.cache_dirty = false;
|
||||
}
|
||||
PrimitiveKind::Border => {
|
||||
let border = &self.cpu_borders[metadata.cpu_prim_index.0];
|
||||
border.write_gpu_blocks(request);
|
||||
}
|
||||
PrimitiveKind::AngleGradient => {
|
||||
let gradient = &mut self.cpu_gradients[metadata.cpu_prim_index.0];
|
||||
if gradient.cache_dirty {
|
||||
let src_stops = display_list.get(gradient.stops_range);
|
||||
|
||||
let dest_gradient = self.gpu_gradient_data.get_mut(gradient.gpu_data_address);
|
||||
dest_gradient.build(src_stops, gradient.reverse_stops);
|
||||
gradient.cache_dirty = false;
|
||||
}
|
||||
PrimitiveKind::BoxShadow => {
|
||||
let box_shadow = &self.cpu_box_shadows[metadata.cpu_prim_index.0];
|
||||
box_shadow.write_gpu_blocks(request);
|
||||
}
|
||||
PrimitiveKind::Image => {
|
||||
let image = &self.cpu_images[metadata.cpu_prim_index.0];
|
||||
image.write_gpu_blocks(request);
|
||||
}
|
||||
PrimitiveKind::YuvImage => {
|
||||
let yuv_image = &self.cpu_yuv_images[metadata.cpu_prim_index.0];
|
||||
yuv_image.write_gpu_blocks(request);
|
||||
}
|
||||
PrimitiveKind::AlignedGradient => {
|
||||
let gradient = &self.cpu_gradients[metadata.cpu_prim_index.0];
|
||||
gradient.build_gpu_blocks_for_aligned(display_list,
|
||||
request);
|
||||
}
|
||||
PrimitiveKind::AngleGradient => {
|
||||
let gradient = &self.cpu_gradients[metadata.cpu_prim_index.0];
|
||||
gradient.build_gpu_blocks_for_angle_radial(display_list,
|
||||
request);
|
||||
}
|
||||
PrimitiveKind::RadialGradient => {
|
||||
let gradient = &self.cpu_radial_gradients[metadata.cpu_prim_index.0];
|
||||
gradient.build_gpu_blocks_for_angle_radial(display_list,
|
||||
request);
|
||||
}
|
||||
PrimitiveKind::TextRun => {
|
||||
let text = &self.cpu_text_runs[metadata.cpu_prim_index.0];
|
||||
text.write_gpu_blocks(request);
|
||||
}
|
||||
PrimitiveKind::RadialGradient => {
|
||||
let gradient = &mut self.cpu_radial_gradients[metadata.cpu_prim_index.0];
|
||||
if gradient.cache_dirty {
|
||||
let src_stops = display_list.get(gradient.stops_range);
|
||||
|
||||
let dest_gradient = self.gpu_gradient_data.get_mut(gradient.gpu_data_address);
|
||||
dest_gradient.build(src_stops, false);
|
||||
gradient.cache_dirty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1332,8 +1429,11 @@ macro_rules! define_gpu_block {
|
|||
)
|
||||
}
|
||||
|
||||
define_gpu_block!(GpuBlock16: [f32; 4] =
|
||||
InstanceRect, GlyphPrimitive
|
||||
);
|
||||
define_gpu_block!(GpuBlock32: [f32; 8] =
|
||||
ClipCorner, ClipRect, ImageMaskData,
|
||||
GradientStopGpu, ClipCorner, ClipRect, ImageMaskData,
|
||||
BorderCornerClipData, BorderCornerDashClipData, BorderCornerDotClipData
|
||||
);
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use debug_render::DebugRenderer;
|
||||
use device::{Device, GpuMarker, GpuSample, NamedTag};
|
||||
use euclid::{Point2D, Size2D, Rect, vec2};
|
||||
use euclid::{Point2D, Size2D, Rect};
|
||||
use std::collections::vec_deque::VecDeque;
|
||||
use std::f32;
|
||||
use std::mem;
|
||||
|
@ -469,7 +469,7 @@ impl ProfileGraph {
|
|||
let stats = self.stats();
|
||||
|
||||
let text_color = ColorF::new(1.0, 1.0, 0.0, 1.0);
|
||||
let text_origin = rect.origin + vec2(rect.size.width, 20.0);
|
||||
let text_origin = rect.origin + Point2D::new(rect.size.width, 20.0);
|
||||
debug_renderer.add_text(text_origin.x,
|
||||
text_origin.y,
|
||||
description,
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
|
||||
use frame::Frame;
|
||||
use frame_builder::FrameBuilderConfig;
|
||||
use gpu_cache::GpuCache;
|
||||
use internal_types::{SourceTexture, ResultMsg, RendererFrame};
|
||||
use internal_types::{FontTemplate, SourceTexture, ResultMsg, RendererFrame};
|
||||
use profiler::{BackendProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
|
||||
use record::ApiRecordingReceiver;
|
||||
use resource_cache::ResourceCache;
|
||||
|
@ -24,7 +23,6 @@ use webrender_traits::{ApiMsg, BlobImageRenderer, BuiltDisplayList, DeviceIntPoi
|
|||
use webrender_traits::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, IdNamespace, ImageData};
|
||||
use webrender_traits::{LayerPoint, PipelineId, RenderDispatcher, RenderNotifier};
|
||||
use webrender_traits::{VRCompositorCommand, VRCompositorHandler, WebGLCommand, WebGLContextId};
|
||||
use webrender_traits::{FontTemplate};
|
||||
|
||||
#[cfg(feature = "webgl")]
|
||||
use offscreen_gl_context::GLContextDispatcher;
|
||||
|
@ -51,7 +49,6 @@ pub struct RenderBackend {
|
|||
inner_rect: DeviceUintRect,
|
||||
next_namespace_id: IdNamespace,
|
||||
|
||||
gpu_cache: GpuCache,
|
||||
resource_cache: ResourceCache,
|
||||
|
||||
scene: Scene,
|
||||
|
@ -100,7 +97,6 @@ impl RenderBackend {
|
|||
pinch_zoom_factor: 1.0,
|
||||
pan: DeviceIntPoint::zero(),
|
||||
resource_cache: resource_cache,
|
||||
gpu_cache: GpuCache::new(),
|
||||
scene: Scene::new(),
|
||||
frame: Frame::new(config),
|
||||
next_namespace_id: IdNamespace(1),
|
||||
|
@ -498,7 +494,6 @@ impl RenderBackend {
|
|||
let pan = LayerPoint::new(self.pan.x as f32 / accumulated_scale_factor,
|
||||
self.pan.y as f32 / accumulated_scale_factor);
|
||||
let frame = self.frame.build(&mut self.resource_cache,
|
||||
&mut self.gpu_cache,
|
||||
&self.scene.display_lists,
|
||||
accumulated_scale_factor,
|
||||
pan,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use gpu_cache::GpuCacheHandle;
|
||||
use gpu_store::GpuStoreAddress;
|
||||
use internal_types::{HardwareCompositeOp, LowLevelFilterOp};
|
||||
use mask_cache::{MaskBounds, MaskCacheInfo};
|
||||
use prim_store::{PrimitiveCacheKey, PrimitiveIndex};
|
||||
|
@ -55,7 +55,7 @@ pub enum AlphaRenderItem {
|
|||
Primitive(Option<ClipScrollGroupIndex>, PrimitiveIndex, i32),
|
||||
Blend(StackingContextIndex, RenderTaskId, LowLevelFilterOp, i32),
|
||||
Composite(StackingContextIndex, RenderTaskId, RenderTaskId, MixBlendMode, i32),
|
||||
SplitComposite(StackingContextIndex, RenderTaskId, GpuCacheHandle, i32),
|
||||
SplitComposite(StackingContextIndex, RenderTaskId, GpuStoreAddress, i32),
|
||||
HardwareComposite(StackingContextIndex, RenderTaskId, HardwareCompositeOp, i32),
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ use debug_render::DebugRenderer;
|
|||
use device::{DepthFunction, Device, FrameId, ProgramId, TextureId, VertexFormat, GpuMarker, GpuProfiler};
|
||||
use device::{GpuSample, TextureFilter, VAOId, VertexUsageHint, FileWatcherHandler, TextureTarget, ShaderError};
|
||||
use device::get_gl_format_bgra;
|
||||
use euclid::Transform3D;
|
||||
use euclid::Matrix4D;
|
||||
use fnv::FnvHasher;
|
||||
use frame_builder::FrameBuilderConfig;
|
||||
use gleam::gl;
|
||||
|
@ -24,6 +24,7 @@ use internal_types::{CacheTextureId, RendererFrame, ResultMsg, TextureUpdateOp};
|
|||
use internal_types::{TextureUpdateList, PackedVertex, RenderTargetMode};
|
||||
use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, SourceTexture};
|
||||
use internal_types::{BatchTextures, TextureSampler};
|
||||
use prim_store::{GradientData, SplitGeometry};
|
||||
use profiler::{Profiler, BackendProfileCounters};
|
||||
use profiler::{GpuProfileTag, RendererProfileTimers, RendererProfileCounters};
|
||||
use record::ApiRecordingReceiver;
|
||||
|
@ -54,7 +55,8 @@ use webgl_types::GLContextHandleWrapper;
|
|||
use webrender_traits::{ColorF, Epoch, PipelineId, RenderNotifier, RenderDispatcher};
|
||||
use webrender_traits::{ExternalImageId, ExternalImageType, ImageData, ImageFormat, RenderApiSender};
|
||||
use webrender_traits::{DeviceIntRect, DeviceUintRect, DevicePoint, DeviceIntPoint, DeviceIntSize, DeviceUintSize};
|
||||
use webrender_traits::{BlobImageRenderer, channel, FontRenderMode};
|
||||
use webrender_traits::{ImageDescriptor, BlobImageRenderer};
|
||||
use webrender_traits::{channel, FontRenderMode};
|
||||
use webrender_traits::VRCompositorHandler;
|
||||
use webrender_traits::{YuvColorSpace, YuvFormat};
|
||||
use webrender_traits::{YUV_COLOR_SPACES, YUV_FORMATS};
|
||||
|
@ -193,53 +195,37 @@ pub enum BlendMode {
|
|||
|
||||
/// The device-specific representation of the cache texture in gpu_cache.rs
|
||||
struct CacheTexture {
|
||||
current_id: TextureId,
|
||||
next_id: TextureId,
|
||||
id: TextureId,
|
||||
}
|
||||
|
||||
impl CacheTexture {
|
||||
fn new(device: &mut Device) -> CacheTexture {
|
||||
let ids = device.create_texture_ids(2, TextureTarget::Default);
|
||||
let id = device.create_texture_ids(1, TextureTarget::Default)[0];
|
||||
|
||||
CacheTexture {
|
||||
current_id: ids[0],
|
||||
next_id: ids[1],
|
||||
id: id,
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, device: &mut Device, updates: &GpuCacheUpdateList) {
|
||||
// See if we need to create or resize the texture.
|
||||
let current_dimensions = device.get_texture_dimensions(self.current_id);
|
||||
let current_dimensions = device.get_texture_dimensions(self.id);
|
||||
|
||||
if updates.height > current_dimensions.height {
|
||||
// TODO(gw): Handle resizing an existing cache texture.
|
||||
if current_dimensions.height > 0 {
|
||||
panic!("TODO: Implement texture copy!!!");
|
||||
}
|
||||
|
||||
// Create a f32 texture that can be used for the vertex shader
|
||||
// to fetch data from.
|
||||
device.init_texture(self.next_id,
|
||||
device.init_texture(self.id,
|
||||
MAX_VERTEX_TEXTURE_WIDTH as u32,
|
||||
updates.height as u32,
|
||||
ImageFormat::RGBAF32,
|
||||
TextureFilter::Nearest,
|
||||
RenderTargetMode::SimpleRenderTarget,
|
||||
RenderTargetMode::None,
|
||||
None);
|
||||
|
||||
// Copy the current texture into the newly resized texture.
|
||||
if current_dimensions.height > 0 {
|
||||
device.bind_draw_target(Some((self.next_id, 0)), None);
|
||||
|
||||
let blit_rect = DeviceIntRect::new(DeviceIntPoint::zero(),
|
||||
DeviceIntSize::new(MAX_VERTEX_TEXTURE_WIDTH as i32,
|
||||
current_dimensions.height as i32));
|
||||
|
||||
// TODO(gw): Should probably switch this to glCopyTexSubImage2D, since we
|
||||
// don't do any stretching here.
|
||||
device.blit_render_target(Some((self.current_id, 0)),
|
||||
Some(blit_rect),
|
||||
blit_rect);
|
||||
|
||||
// Free the GPU memory for that texture until we need to resize again.
|
||||
device.deinit_texture(self.current_id);
|
||||
}
|
||||
|
||||
mem::swap(&mut self.current_id, &mut self.next_id);
|
||||
}
|
||||
|
||||
for update in &updates.updates {
|
||||
|
@ -258,7 +244,7 @@ impl CacheTexture {
|
|||
.offset(block_index as isize);
|
||||
slice::from_raw_parts(ptr as *const _, block_count * 16)
|
||||
};
|
||||
device.update_texture(self.current_id,
|
||||
device.update_texture(self.id,
|
||||
address.u as u32,
|
||||
address.v as u32,
|
||||
block_count as u32,
|
||||
|
@ -340,6 +326,45 @@ impl GpuStoreLayout for VertexDataTextureLayout {
|
|||
type VertexDataTexture = GpuDataTexture<VertexDataTextureLayout>;
|
||||
pub type VertexDataStore<T> = GpuStore<T, VertexDataTextureLayout>;
|
||||
|
||||
pub struct GradientDataTextureLayout;
|
||||
|
||||
impl GpuStoreLayout for GradientDataTextureLayout {
|
||||
fn image_format() -> ImageFormat {
|
||||
ImageFormat::RGBA8
|
||||
}
|
||||
|
||||
fn texture_width<T>() -> usize {
|
||||
mem::size_of::<GradientData>() / Self::texel_size() / 2
|
||||
}
|
||||
|
||||
fn texture_filter() -> TextureFilter {
|
||||
TextureFilter::Linear
|
||||
}
|
||||
}
|
||||
|
||||
type GradientDataTexture = GpuDataTexture<GradientDataTextureLayout>;
|
||||
pub type GradientDataStore = GpuStore<GradientData, GradientDataTextureLayout>;
|
||||
|
||||
pub struct SplitGeometryTextureLayout;
|
||||
|
||||
impl GpuStoreLayout for SplitGeometryTextureLayout {
|
||||
fn image_format() -> ImageFormat {
|
||||
//TODO: use normalized integers
|
||||
ImageFormat::RGBAF32
|
||||
}
|
||||
|
||||
fn texture_width<T>() -> usize {
|
||||
MAX_VERTEX_TEXTURE_WIDTH - (MAX_VERTEX_TEXTURE_WIDTH % Self::texels_per_item::<T>())
|
||||
}
|
||||
|
||||
fn texture_filter() -> TextureFilter {
|
||||
TextureFilter::Nearest
|
||||
}
|
||||
}
|
||||
|
||||
type SplitGeometryTexture = GpuDataTexture<SplitGeometryTextureLayout>;
|
||||
pub type SplitGeometryStore = GpuStore<SplitGeometry, SplitGeometryTextureLayout>;
|
||||
|
||||
const TRANSFORM_FEATURE: &'static str = "TRANSFORM";
|
||||
const SUBPIXEL_AA_FEATURE: &'static str = "SUBPIXEL_AA";
|
||||
const CLIP_FEATURE: &'static str = "CLIP";
|
||||
|
@ -505,8 +530,12 @@ fn create_clip_shader(name: &'static str, device: &mut Device) -> Result<Program
|
|||
struct GpuDataTextures {
|
||||
layer_texture: VertexDataTexture,
|
||||
render_task_texture: VertexDataTexture,
|
||||
prim_geom_texture: VertexDataTexture,
|
||||
data16_texture: VertexDataTexture,
|
||||
data32_texture: VertexDataTexture,
|
||||
resource_rects_texture: VertexDataTexture,
|
||||
gradient_data_texture: GradientDataTexture,
|
||||
split_geometry_texture: SplitGeometryTexture,
|
||||
}
|
||||
|
||||
impl GpuDataTextures {
|
||||
|
@ -514,21 +543,33 @@ impl GpuDataTextures {
|
|||
GpuDataTextures {
|
||||
layer_texture: VertexDataTexture::new(device),
|
||||
render_task_texture: VertexDataTexture::new(device),
|
||||
prim_geom_texture: VertexDataTexture::new(device),
|
||||
data16_texture: VertexDataTexture::new(device),
|
||||
data32_texture: VertexDataTexture::new(device),
|
||||
resource_rects_texture: VertexDataTexture::new(device),
|
||||
gradient_data_texture: GradientDataTexture::new(device),
|
||||
split_geometry_texture: SplitGeometryTexture::new(device),
|
||||
}
|
||||
}
|
||||
|
||||
fn init_frame(&mut self, device: &mut Device, frame: &mut Frame) {
|
||||
self.data16_texture.init(device, &mut frame.gpu_data16);
|
||||
self.data32_texture.init(device, &mut frame.gpu_data32);
|
||||
self.prim_geom_texture.init(device, &mut frame.gpu_geometry);
|
||||
self.resource_rects_texture.init(device, &mut frame.gpu_resource_rects);
|
||||
self.layer_texture.init(device, &mut frame.layer_texture_data);
|
||||
self.render_task_texture.init(device, &mut frame.render_task_data);
|
||||
self.gradient_data_texture.init(device, &mut frame.gpu_gradient_data);
|
||||
self.split_geometry_texture.init(device, &mut frame.gpu_split_geometry);
|
||||
|
||||
device.bind_texture(TextureSampler::Layers, self.layer_texture.id);
|
||||
device.bind_texture(TextureSampler::RenderTasks, self.render_task_texture.id);
|
||||
device.bind_texture(TextureSampler::Geometry, self.prim_geom_texture.id);
|
||||
device.bind_texture(TextureSampler::Data16, self.data16_texture.id);
|
||||
device.bind_texture(TextureSampler::Data32, self.data32_texture.id);
|
||||
device.bind_texture(TextureSampler::ResourceRects, self.resource_rects_texture.id);
|
||||
device.bind_texture(TextureSampler::Gradients, self.gradient_data_texture.id);
|
||||
device.bind_texture(TextureSampler::SplitGeometry, self.split_geometry_texture.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -951,8 +992,34 @@ impl Renderer {
|
|||
let device_max_size = device.max_texture_size();
|
||||
let max_texture_size = cmp::min(device_max_size, options.max_texture_size.unwrap_or(device_max_size));
|
||||
|
||||
let texture_cache = TextureCache::new(max_texture_size);
|
||||
let backend_profile_counters = BackendProfileCounters::new();
|
||||
let mut texture_cache = TextureCache::new(max_texture_size);
|
||||
let mut backend_profile_counters = BackendProfileCounters::new();
|
||||
|
||||
let white_pixels: Vec<u8> = vec![
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff,
|
||||
];
|
||||
let mask_pixels: Vec<u8> = vec![
|
||||
0xff, 0xff,
|
||||
0xff, 0xff,
|
||||
];
|
||||
|
||||
// TODO: Ensure that the white texture can never get evicted when the cache supports LRU eviction!
|
||||
let white_image_id = texture_cache.new_item_id();
|
||||
texture_cache.insert(white_image_id,
|
||||
ImageDescriptor::new(2, 2, ImageFormat::RGBA8, false),
|
||||
TextureFilter::Linear,
|
||||
ImageData::Raw(Arc::new(white_pixels)),
|
||||
&mut backend_profile_counters.resources.texture_cache);
|
||||
|
||||
let dummy_mask_image_id = texture_cache.new_item_id();
|
||||
texture_cache.insert(dummy_mask_image_id,
|
||||
ImageDescriptor::new(2, 2, ImageFormat::A8, false),
|
||||
TextureFilter::Linear,
|
||||
ImageData::Raw(Arc::new(mask_pixels)),
|
||||
&mut backend_profile_counters.resources.texture_cache);
|
||||
|
||||
let dummy_cache_texture_id = device.create_texture_ids(1, TextureTarget::Array)[0];
|
||||
device.init_texture(dummy_cache_texture_id,
|
||||
|
@ -1317,7 +1384,7 @@ impl Renderer {
|
|||
self.update_texture_cache();
|
||||
|
||||
self.update_gpu_cache();
|
||||
self.device.bind_texture(TextureSampler::ResourceCache, self.gpu_cache_texture.current_id);
|
||||
self.device.bind_texture(TextureSampler::ResourceCache, self.gpu_cache_texture.id);
|
||||
|
||||
frame_id
|
||||
};
|
||||
|
@ -1519,7 +1586,7 @@ impl Renderer {
|
|||
vao: VAOId,
|
||||
shader: ProgramId,
|
||||
textures: &BatchTextures,
|
||||
projection: &Transform3D<f32>) {
|
||||
projection: &Matrix4D<f32>) {
|
||||
self.device.bind_vao(vao);
|
||||
self.device.bind_program(shader, projection);
|
||||
|
||||
|
@ -1550,7 +1617,7 @@ impl Renderer {
|
|||
|
||||
fn submit_batch(&mut self,
|
||||
batch: &PrimitiveBatch,
|
||||
projection: &Transform3D<f32>,
|
||||
projection: &Matrix4D<f32>,
|
||||
render_task_data: &[RenderTaskData],
|
||||
cache_texture: TextureId,
|
||||
render_target: Option<(TextureId, i32)>,
|
||||
|
@ -1714,7 +1781,7 @@ impl Renderer {
|
|||
color_cache_texture: TextureId,
|
||||
clear_color: Option<[f32; 4]>,
|
||||
render_task_data: &[RenderTaskData],
|
||||
projection: &Transform3D<f32>) {
|
||||
projection: &Matrix4D<f32>) {
|
||||
{
|
||||
let _gm = self.gpu_profile.add_marker(GPU_TAG_SETUP_TARGET);
|
||||
self.device.bind_draw_target(render_target, Some(target_size));
|
||||
|
@ -1858,7 +1925,7 @@ impl Renderer {
|
|||
render_target: (TextureId, i32),
|
||||
target: &AlphaRenderTarget,
|
||||
target_size: DeviceUintSize,
|
||||
projection: &Transform3D<f32>) {
|
||||
projection: &Matrix4D<f32>) {
|
||||
{
|
||||
let _gm = self.gpu_profile.add_marker(GPU_TAG_SETUP_TARGET);
|
||||
self.device.bind_draw_target(Some(render_target), Some(target_size));
|
||||
|
@ -2096,7 +2163,7 @@ impl Renderer {
|
|||
None
|
||||
};
|
||||
size = framebuffer_size;
|
||||
projection = Transform3D::ortho(0.0,
|
||||
projection = Matrix4D::ortho(0.0,
|
||||
size.width as f32,
|
||||
size.height as f32,
|
||||
0.0,
|
||||
|
@ -2105,7 +2172,7 @@ impl Renderer {
|
|||
} else {
|
||||
size = &frame.cache_size;
|
||||
clear_color = Some([0.0, 0.0, 0.0, 0.0]);
|
||||
projection = Transform3D::ortho(0.0,
|
||||
projection = Matrix4D::ortho(0.0,
|
||||
size.width as f32,
|
||||
0.0,
|
||||
size.height as f32,
|
||||
|
|
|
@ -6,7 +6,8 @@ use app_units::Au;
|
|||
use device::TextureFilter;
|
||||
use fnv::FnvHasher;
|
||||
use frame::FrameId;
|
||||
use internal_types::{SourceTexture, TextureUpdateList};
|
||||
use gpu_cache::GpuCache;
|
||||
use internal_types::{FontTemplate, SourceTexture, TextureUpdateList};
|
||||
use profiler::TextureCacheProfileCounters;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::hash_map::Entry::{self, Occupied, Vacant};
|
||||
|
@ -16,12 +17,11 @@ use std::hash::Hash;
|
|||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
use texture_cache::{TextureCache, TextureCacheItemId};
|
||||
use webrender_traits::{Epoch, FontKey, FontTemplate, GlyphKey, ImageKey, ImageRendering};
|
||||
use webrender_traits::{Epoch, FontKey, GlyphKey, ImageKey, ImageRendering};
|
||||
use webrender_traits::{FontRenderMode, ImageData, GlyphDimensions, WebGLContextId};
|
||||
use webrender_traits::{DevicePoint, DeviceIntSize, DeviceUintRect, ImageDescriptor, ColorF};
|
||||
use webrender_traits::{GlyphOptions, GlyphInstance, TileOffset, TileSize};
|
||||
use webrender_traits::{BlobImageRenderer, BlobImageDescriptor, BlobImageError, BlobImageRequest, BlobImageData};
|
||||
use webrender_traits::BlobImageResources;
|
||||
use webrender_traits::{BlobImageRenderer, BlobImageDescriptor, BlobImageError, BlobImageRequest, BlobImageData, ImageStore};
|
||||
use webrender_traits::{ExternalImageData, ExternalImageType, LayoutPoint};
|
||||
use rayon::ThreadPool;
|
||||
use glyph_rasterizer::{GlyphRasterizer, GlyphCache, GlyphRequest};
|
||||
|
@ -92,6 +92,12 @@ impl ImageTemplates {
|
|||
}
|
||||
}
|
||||
|
||||
impl ImageStore for ImageTemplates {
|
||||
fn get_image(&self, key: ImageKey) -> Option<(&ImageData, &ImageDescriptor)> {
|
||||
self.images.get(&key).map(|resource|{ (&resource.data, &resource.descriptor) })
|
||||
}
|
||||
}
|
||||
|
||||
struct CachedImageInfo {
|
||||
texture_cache_id: TextureCacheItemId,
|
||||
epoch: Epoch,
|
||||
|
@ -180,20 +186,6 @@ struct WebGLTexture {
|
|||
size: DeviceIntSize,
|
||||
}
|
||||
|
||||
struct Resources {
|
||||
font_templates: HashMap<FontKey, FontTemplate, BuildHasherDefault<FnvHasher>>,
|
||||
image_templates: ImageTemplates,
|
||||
}
|
||||
|
||||
impl BlobImageResources for Resources {
|
||||
fn get_font_data(&self, key: FontKey) -> &FontTemplate {
|
||||
self.font_templates.get(&key).unwrap()
|
||||
}
|
||||
fn get_image(&self, key: ImageKey) -> Option<(&ImageData, &ImageDescriptor)> {
|
||||
self.image_templates.get(key).map(|resource| { (&resource.data, &resource.descriptor) })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ResourceCache {
|
||||
cached_glyphs: GlyphCache,
|
||||
cached_images: ResourceClassCache<ImageRequest, CachedImageInfo>,
|
||||
|
@ -201,11 +193,13 @@ pub struct ResourceCache {
|
|||
// TODO(pcwalton): Figure out the lifecycle of these.
|
||||
webgl_textures: HashMap<WebGLContextId, WebGLTexture, BuildHasherDefault<FnvHasher>>,
|
||||
|
||||
resources: Resources,
|
||||
font_templates: HashMap<FontKey, FontTemplate, BuildHasherDefault<FnvHasher>>,
|
||||
image_templates: ImageTemplates,
|
||||
state: State,
|
||||
current_frame_id: FrameId,
|
||||
|
||||
texture_cache: TextureCache,
|
||||
pub gpu_cache: GpuCache,
|
||||
|
||||
// TODO(gw): We should expire (parts of) this cache semi-regularly!
|
||||
cached_glyph_dimensions: HashMap<GlyphKey, Option<GlyphDimensions>, BuildHasherDefault<FnvHasher>>,
|
||||
|
@ -224,12 +218,11 @@ impl ResourceCache {
|
|||
cached_glyphs: ResourceClassCache::new(),
|
||||
cached_images: ResourceClassCache::new(),
|
||||
webgl_textures: HashMap::default(),
|
||||
resources: Resources {
|
||||
font_templates: HashMap::default(),
|
||||
image_templates: ImageTemplates::new(),
|
||||
},
|
||||
font_templates: HashMap::default(),
|
||||
image_templates: ImageTemplates::new(),
|
||||
cached_glyph_dimensions: HashMap::default(),
|
||||
texture_cache: texture_cache,
|
||||
gpu_cache: GpuCache::new(),
|
||||
state: State::Idle,
|
||||
current_frame_id: FrameId(0),
|
||||
pending_image_requests: Vec::new(),
|
||||
|
@ -261,15 +254,12 @@ impl ResourceCache {
|
|||
// Push the new font to the font renderer, and also store
|
||||
// it locally for glyph metric requests.
|
||||
self.glyph_rasterizer.add_font(font_key, template.clone());
|
||||
self.resources.font_templates.insert(font_key, template);
|
||||
self.font_templates.insert(font_key, template);
|
||||
}
|
||||
|
||||
pub fn delete_font_template(&mut self, font_key: FontKey) {
|
||||
self.glyph_rasterizer.delete_font(font_key);
|
||||
self.resources.font_templates.remove(&font_key);
|
||||
if let Some(ref mut r) = self.blob_image_renderer {
|
||||
r.delete_font(font_key);
|
||||
}
|
||||
self.font_templates.remove(&font_key);
|
||||
}
|
||||
|
||||
pub fn add_image_template(&mut self,
|
||||
|
@ -299,7 +289,7 @@ impl ResourceCache {
|
|||
dirty_rect: None,
|
||||
};
|
||||
|
||||
self.resources.image_templates.insert(image_key, resource);
|
||||
self.image_templates.insert(image_key, resource);
|
||||
}
|
||||
|
||||
pub fn update_image_template(&mut self,
|
||||
|
@ -307,7 +297,7 @@ impl ResourceCache {
|
|||
descriptor: ImageDescriptor,
|
||||
mut data: ImageData,
|
||||
dirty_rect: Option<DeviceUintRect>) {
|
||||
let resource = if let Some(image) = self.resources.image_templates.get(image_key) {
|
||||
let resource = if let Some(image) = self.image_templates.get(image_key) {
|
||||
assert_eq!(image.descriptor.width, descriptor.width);
|
||||
assert_eq!(image.descriptor.height, descriptor.height);
|
||||
assert_eq!(image.descriptor.format, descriptor.format);
|
||||
|
@ -341,11 +331,11 @@ impl ResourceCache {
|
|||
panic!("Attempt to update non-existant image (key {:?}).", image_key);
|
||||
};
|
||||
|
||||
self.resources.image_templates.insert(image_key, resource);
|
||||
self.image_templates.insert(image_key, resource);
|
||||
}
|
||||
|
||||
pub fn delete_image_template(&mut self, image_key: ImageKey) {
|
||||
let value = self.resources.image_templates.remove(image_key);
|
||||
let value = self.image_templates.remove(image_key);
|
||||
|
||||
match value {
|
||||
Some(image) => {
|
||||
|
@ -386,7 +376,7 @@ impl ResourceCache {
|
|||
tile: tile,
|
||||
};
|
||||
|
||||
let template = self.resources.image_templates.get(key).unwrap();
|
||||
let template = self.image_templates.get(key).unwrap();
|
||||
if template.data.uses_texture_cache() {
|
||||
self.cached_images.mark_as_needed(&request, self.current_frame_id);
|
||||
}
|
||||
|
@ -415,7 +405,6 @@ impl ResourceCache {
|
|||
};
|
||||
|
||||
renderer.request(
|
||||
&self.resources,
|
||||
request.into(),
|
||||
&BlobImageDescriptor {
|
||||
width: w,
|
||||
|
@ -424,6 +413,7 @@ impl ResourceCache {
|
|||
format: template.descriptor.format,
|
||||
},
|
||||
template.dirty_rect,
|
||||
&self.image_templates,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -529,7 +519,7 @@ impl ResourceCache {
|
|||
}
|
||||
|
||||
pub fn get_image_properties(&self, image_key: ImageKey) -> ImageProperties {
|
||||
let image_template = &self.resources.image_templates.get(image_key).unwrap();
|
||||
let image_template = &self.image_templates.get(image_key).unwrap();
|
||||
|
||||
let external_image = match image_template.data {
|
||||
ImageData::External(ext_image) => {
|
||||
|
@ -577,6 +567,7 @@ impl ResourceCache {
|
|||
debug_assert_eq!(self.state, State::Idle);
|
||||
self.state = State::AddResources;
|
||||
self.current_frame_id = frame_id;
|
||||
self.gpu_cache.begin_frame();
|
||||
}
|
||||
|
||||
pub fn block_until_all_resources_added(&mut self,
|
||||
|
@ -632,7 +623,7 @@ impl ResourceCache {
|
|||
request: &ImageRequest,
|
||||
image_data: Option<ImageData>,
|
||||
texture_cache_profile: &mut TextureCacheProfileCounters) {
|
||||
let image_template = self.resources.image_templates.get_mut(request.key).unwrap();
|
||||
let image_template = self.image_templates.get_mut(request.key).unwrap();
|
||||
let image_data = image_data.unwrap_or_else(||{
|
||||
image_template.data.clone()
|
||||
});
|
||||
|
@ -689,15 +680,18 @@ impl ResourceCache {
|
|||
}
|
||||
}
|
||||
Vacant(entry) => {
|
||||
let image_id = self.texture_cache.new_item_id();
|
||||
|
||||
let filter = match request.rendering {
|
||||
ImageRendering::Pixelated => TextureFilter::Nearest,
|
||||
ImageRendering::Auto | ImageRendering::CrispEdges => TextureFilter::Linear,
|
||||
};
|
||||
|
||||
let image_id = self.texture_cache.insert(descriptor,
|
||||
filter,
|
||||
image_data,
|
||||
texture_cache_profile);
|
||||
self.texture_cache.insert(image_id,
|
||||
descriptor,
|
||||
filter,
|
||||
image_data,
|
||||
texture_cache_profile);
|
||||
|
||||
entry.insert(CachedImageInfo {
|
||||
texture_cache_id: image_id,
|
||||
|
@ -710,7 +704,7 @@ impl ResourceCache {
|
|||
request: ImageRequest,
|
||||
image_data: Option<ImageData>,
|
||||
texture_cache_profile: &mut TextureCacheProfileCounters) {
|
||||
match self.resources.image_templates.get(request.key).unwrap().data {
|
||||
match self.image_templates.get(request.key).unwrap().data {
|
||||
ImageData::External(ext_image) => {
|
||||
match ext_image.image_type {
|
||||
ExternalImageType::Texture2DHandle |
|
||||
|
|
|
@ -445,6 +445,9 @@ pub struct TextureCacheItem {
|
|||
// The texture coordinates for this item
|
||||
pub pixel_rect: RectUv<i32, DevicePixel>,
|
||||
|
||||
// The size of the entire texture (not just the allocated rectangle)
|
||||
pub texture_size: DeviceUintSize,
|
||||
|
||||
// The size of the allocated rectangle.
|
||||
pub allocated_rect: DeviceUintRect,
|
||||
}
|
||||
|
@ -483,10 +486,12 @@ impl FreeListItem for TextureCacheItem {
|
|||
|
||||
impl TextureCacheItem {
|
||||
fn new(texture_id: CacheTextureId,
|
||||
rect: DeviceUintRect)
|
||||
rect: DeviceUintRect,
|
||||
texture_size: &DeviceUintSize)
|
||||
-> TextureCacheItem {
|
||||
TextureCacheItem {
|
||||
texture_id: texture_id,
|
||||
texture_size: *texture_size,
|
||||
pixel_rect: RectUv {
|
||||
top_left: DeviceIntPoint::new(rect.origin.x as i32,
|
||||
rect.origin.y as i32),
|
||||
|
@ -578,7 +583,6 @@ pub enum AllocationKind {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct AllocationResult {
|
||||
image_id: TextureCacheItemId,
|
||||
kind: AllocationKind,
|
||||
item: TextureCacheItem,
|
||||
}
|
||||
|
@ -607,7 +611,18 @@ impl TextureCache {
|
|||
mem::replace(&mut self.pending_updates, TextureUpdateList::new())
|
||||
}
|
||||
|
||||
// TODO(gw): This API is a bit ugly (having to allocate an ID and
|
||||
// then use it). But it has to be that way for now due to
|
||||
// how the raster_jobs code works.
|
||||
pub fn new_item_id(&mut self) -> TextureCacheItemId {
|
||||
let new_item = TextureCacheItem::new(CacheTextureId(0),
|
||||
DeviceUintRect::zero(),
|
||||
&DeviceUintSize::zero());
|
||||
self.items.insert(new_item)
|
||||
}
|
||||
|
||||
pub fn allocate(&mut self,
|
||||
image_id: TextureCacheItemId,
|
||||
requested_width: u32,
|
||||
requested_height: u32,
|
||||
format: ImageFormat,
|
||||
|
@ -626,13 +641,13 @@ impl TextureCache {
|
|||
let texture_id = self.cache_id_list.allocate();
|
||||
let cache_item = TextureCacheItem::new(
|
||||
texture_id,
|
||||
DeviceUintRect::new(DeviceUintPoint::zero(), requested_size));
|
||||
let image_id = self.items.insert(cache_item);
|
||||
DeviceUintRect::new(DeviceUintPoint::zero(), requested_size),
|
||||
&requested_size);
|
||||
*self.items.get_mut(image_id) = cache_item;
|
||||
|
||||
return AllocationResult {
|
||||
item: self.items.get(image_id).clone(),
|
||||
kind: AllocationKind::Standalone,
|
||||
image_id: image_id,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -676,6 +691,12 @@ impl TextureCache {
|
|||
|
||||
page.grow(texture_size);
|
||||
|
||||
self.items.for_each_item(|item| {
|
||||
if item.texture_id == page.texture_id {
|
||||
item.texture_size = texture_size;
|
||||
}
|
||||
});
|
||||
|
||||
if page.can_allocate(&requested_size) {
|
||||
page_id = Some(i);
|
||||
break;
|
||||
|
@ -722,13 +743,13 @@ impl TextureCache {
|
|||
let location = page.allocate(&requested_size)
|
||||
.expect("All the checks have passed till now, there is no way back.");
|
||||
let cache_item = TextureCacheItem::new(page.texture_id,
|
||||
DeviceUintRect::new(location, requested_size));
|
||||
let image_id = self.items.insert(cache_item.clone());
|
||||
DeviceUintRect::new(location, requested_size),
|
||||
&page.texture_size);
|
||||
*self.items.get_mut(image_id) = cache_item.clone();
|
||||
|
||||
AllocationResult {
|
||||
item: cache_item,
|
||||
kind: AllocationKind::TexturePage,
|
||||
image_id: image_id,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -789,10 +810,11 @@ impl TextureCache {
|
|||
}
|
||||
|
||||
pub fn insert(&mut self,
|
||||
image_id: TextureCacheItemId,
|
||||
descriptor: ImageDescriptor,
|
||||
filter: TextureFilter,
|
||||
data: ImageData,
|
||||
profile: &mut TextureCacheProfileCounters) -> TextureCacheItemId {
|
||||
profile: &mut TextureCacheProfileCounters) {
|
||||
if let ImageData::Blob(..) = data {
|
||||
panic!("must rasterize the vector image before adding to the cache");
|
||||
}
|
||||
|
@ -809,7 +831,8 @@ impl TextureCache {
|
|||
assert!(vec.len() >= finish as usize);
|
||||
}
|
||||
|
||||
let result = self.allocate(width,
|
||||
let result = self.allocate(image_id,
|
||||
width,
|
||||
height,
|
||||
format,
|
||||
filter,
|
||||
|
@ -906,8 +929,6 @@ impl TextureCache {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.image_id
|
||||
}
|
||||
|
||||
pub fn get(&self, id: TextureCacheItemId) -> &TextureCacheItem {
|
||||
|
|
|
@ -6,13 +6,13 @@ use app_units::Au;
|
|||
use border::{BorderCornerInstance, BorderCornerSide};
|
||||
use device::TextureId;
|
||||
use fnv::FnvHasher;
|
||||
use gpu_cache::{GpuCache, GpuCacheUpdateList};
|
||||
use gpu_cache::GpuCacheUpdateList;
|
||||
use gpu_store::GpuStoreAddress;
|
||||
use internal_types::{ANGLE_FLOAT_TO_FIXED, BatchTextures, CacheTextureId, LowLevelFilterOp};
|
||||
use internal_types::SourceTexture;
|
||||
use mask_cache::MaskCacheInfo;
|
||||
use prim_store::{CLIP_DATA_GPU_SIZE, DeferredResolve, GpuBlock32};
|
||||
use prim_store::PrimitiveCacheKey;
|
||||
use prim_store::{CLIP_DATA_GPU_SIZE, DeferredResolve, GpuBlock16, GpuBlock32};
|
||||
use prim_store::{GradientData, SplitGeometry, PrimitiveCacheKey, PrimitiveGeometry};
|
||||
use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore, TexelRect};
|
||||
use profiler::FrameProfileCounters;
|
||||
use render_task::{AlphaRenderItem, MaskGeometryKind, MaskSegment, RenderTask, RenderTaskData};
|
||||
|
@ -28,9 +28,9 @@ use texture_cache::TexturePage;
|
|||
use util::{TransformedRect, TransformedRectKind};
|
||||
use webrender_traits::{BuiltDisplayList, ClipAndScrollInfo, ClipId, ColorF, DeviceIntPoint};
|
||||
use webrender_traits::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize};
|
||||
use webrender_traits::{ExternalImageType, FontRenderMode, ImageRendering, LayerRect};
|
||||
use webrender_traits::{ExternalImageType, FontRenderMode, ImageRendering, LayerPoint, LayerRect};
|
||||
use webrender_traits::{LayerToWorldTransform, MixBlendMode, PipelineId, TransformStyle};
|
||||
use webrender_traits::{WorldToLayerTransform, YuvColorSpace, YuvFormat, LayerVector2D};
|
||||
use webrender_traits::{WorldToLayerTransform, YuvColorSpace, YuvFormat};
|
||||
|
||||
// Special sentinel value recognized by the shader. It is considered to be
|
||||
// a dummy task that doesn't mask out anything.
|
||||
|
@ -425,9 +425,10 @@ impl AlphaRenderItem {
|
|||
let blend_mode = ctx.prim_store.get_blend_mode(needs_blending, prim_metadata);
|
||||
|
||||
let prim_cache_address = prim_metadata.gpu_location
|
||||
.as_int(&ctx.gpu_cache);
|
||||
.as_int(&ctx.resource_cache.gpu_cache);
|
||||
|
||||
let base_instance = SimplePrimitiveInstance::new(prim_cache_address,
|
||||
let base_instance = SimplePrimitiveInstance::new(prim_index,
|
||||
prim_cache_address,
|
||||
task_index,
|
||||
clip_task_index,
|
||||
packed_layer_index,
|
||||
|
@ -516,33 +517,38 @@ impl AlphaRenderItem {
|
|||
None => 0,
|
||||
};
|
||||
|
||||
let user_data1 = match batch_kind {
|
||||
AlphaBatchKind::TextRun => text_cpu.resource_address.0,
|
||||
AlphaBatchKind::CacheImage => cache_task_index,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
for glyph_index in 0..text_cpu.gpu_data_count {
|
||||
let user_data1 = match batch_kind {
|
||||
AlphaBatchKind::TextRun => text_cpu.resource_address.0 + glyph_index,
|
||||
AlphaBatchKind::CacheImage => cache_task_index,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
for glyph_index in 0..text_cpu.glyph_instances.len() {
|
||||
batch.add_instance(base_instance.build(glyph_index as i32, user_data1));
|
||||
batch.add_instance(base_instance.build(text_cpu.gpu_data_address.0 + glyph_index,
|
||||
user_data1));
|
||||
}
|
||||
}
|
||||
PrimitiveKind::AlignedGradient => {
|
||||
let gradient_cpu = &ctx.prim_store.cpu_gradients[prim_metadata.cpu_prim_index.0];
|
||||
let key = AlphaBatchKey::new(AlphaBatchKind::AlignedGradient, flags, blend_mode, textures);
|
||||
let batch = batch_list.get_suitable_batch(&key, item_bounding_rect);
|
||||
for part_index in 0..(gradient_cpu.stops_count - 1) {
|
||||
batch.add_instance(base_instance.build(part_index as i32, 0));
|
||||
for part_index in 0..(gradient_cpu.gpu_data_count - 1) {
|
||||
batch.add_instance(base_instance.build(gradient_cpu.gpu_data_address.0 + part_index, 0));
|
||||
}
|
||||
}
|
||||
PrimitiveKind::AngleGradient => {
|
||||
let gradient_cpu = &ctx.prim_store.cpu_gradients[prim_metadata.cpu_prim_index.0];
|
||||
let key = AlphaBatchKey::new(AlphaBatchKind::AngleGradient, flags, blend_mode, textures);
|
||||
let batch = batch_list.get_suitable_batch(&key, item_bounding_rect);
|
||||
batch.add_instance(base_instance.build(0, 0));
|
||||
batch.add_instance(base_instance.build(gradient_cpu.gpu_data_address.0,
|
||||
0));
|
||||
}
|
||||
PrimitiveKind::RadialGradient => {
|
||||
let gradient_cpu = &ctx.prim_store.cpu_radial_gradients[prim_metadata.cpu_prim_index.0];
|
||||
let key = AlphaBatchKey::new(AlphaBatchKind::RadialGradient, flags, blend_mode, textures);
|
||||
let batch = batch_list.get_suitable_batch(&key, item_bounding_rect);
|
||||
batch.add_instance(base_instance.build(0, 0));
|
||||
batch.add_instance(base_instance.build(gradient_cpu.gpu_data_address.0,
|
||||
0));
|
||||
}
|
||||
PrimitiveKind::YuvImage => {
|
||||
let image_yuv_cpu = &ctx.prim_store.cpu_yuv_images[prim_metadata.cpu_prim_index.0];
|
||||
|
@ -598,7 +604,7 @@ impl AlphaRenderItem {
|
|||
}
|
||||
}
|
||||
}
|
||||
AlphaRenderItem::SplitComposite(sc_index, task_id, gpu_handle, z) => {
|
||||
AlphaRenderItem::SplitComposite(sc_index, task_id, gpu_address, z) => {
|
||||
let key = AlphaBatchKey::new(AlphaBatchKind::SplitComposite,
|
||||
AlphaBatchKeyFlags::empty(),
|
||||
BlendMode::PremultipliedAlpha,
|
||||
|
@ -606,12 +612,11 @@ impl AlphaRenderItem {
|
|||
let stacking_context = &ctx.stacking_context_store[sc_index.0];
|
||||
let batch = batch_list.get_suitable_batch(&key, &stacking_context.screen_bounds);
|
||||
let source_task = render_tasks.get_task_index(&task_id, child_pass_index);
|
||||
let gpu_address = gpu_handle.as_int(ctx.gpu_cache);
|
||||
|
||||
let instance = CompositePrimitiveInstance::new(task_index,
|
||||
source_task,
|
||||
RenderTaskIndex(0),
|
||||
gpu_address,
|
||||
gpu_address.0,
|
||||
0,
|
||||
z);
|
||||
|
||||
|
@ -757,7 +762,6 @@ pub struct RenderTargetContext<'a> {
|
|||
pub clip_scroll_group_store: &'a [ClipScrollGroup],
|
||||
pub prim_store: &'a PrimitiveStore,
|
||||
pub resource_cache: &'a ResourceCache,
|
||||
pub gpu_cache: &'a GpuCache,
|
||||
}
|
||||
|
||||
struct TextureAllocator {
|
||||
|
@ -977,11 +981,12 @@ impl RenderTarget for ColorRenderTarget {
|
|||
let prim_metadata = ctx.prim_store.get_metadata(prim_index);
|
||||
|
||||
let prim_address = prim_metadata.gpu_location
|
||||
.as_int(&ctx.gpu_cache);
|
||||
.as_int(&ctx.resource_cache.gpu_cache);
|
||||
|
||||
match prim_metadata.prim_kind {
|
||||
PrimitiveKind::BoxShadow => {
|
||||
let instance = SimplePrimitiveInstance::new(prim_address,
|
||||
let instance = SimplePrimitiveInstance::new(prim_index,
|
||||
prim_address,
|
||||
render_tasks.get_task_index(&task.id, pass_index),
|
||||
RenderTaskIndex(0),
|
||||
PackedLayerIndex(0),
|
||||
|
@ -1005,15 +1010,16 @@ impl RenderTarget for ColorRenderTarget {
|
|||
self.text_run_textures.colors[0] == textures.colors[0]);
|
||||
self.text_run_textures = textures;
|
||||
|
||||
let instance = SimplePrimitiveInstance::new(prim_address,
|
||||
let instance = SimplePrimitiveInstance::new(prim_index,
|
||||
prim_address,
|
||||
render_tasks.get_task_index(&task.id, pass_index),
|
||||
RenderTaskIndex(0),
|
||||
PackedLayerIndex(0),
|
||||
0); // z is disabled for rendering cache primitives
|
||||
|
||||
for glyph_index in 0..text.glyph_instances.len() {
|
||||
self.text_run_cache_prims.push(instance.build(glyph_index as i32,
|
||||
text.resource_address.0));
|
||||
for glyph_index in 0..text.gpu_data_count {
|
||||
self.text_run_cache_prims.push(instance.build(text.gpu_data_address.0 + glyph_index,
|
||||
text.resource_address.0 + glyph_index));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
@ -1301,6 +1307,7 @@ pub struct PrimitiveInstance {
|
|||
}
|
||||
|
||||
struct SimplePrimitiveInstance {
|
||||
pub global_prim_index: i32,
|
||||
// TODO(gw): specific_prim_address is encoded as an i32, since
|
||||
// some primitives use GPU Cache and some still use
|
||||
// GPU Store. Once everything is converted to use the
|
||||
|
@ -1315,12 +1322,14 @@ struct SimplePrimitiveInstance {
|
|||
}
|
||||
|
||||
impl SimplePrimitiveInstance {
|
||||
fn new(specific_prim_address: i32,
|
||||
fn new(prim_index: PrimitiveIndex,
|
||||
specific_prim_address: i32,
|
||||
task_index: RenderTaskIndex,
|
||||
clip_task_index: RenderTaskIndex,
|
||||
layer_index: PackedLayerIndex,
|
||||
z_sort_index: i32) -> SimplePrimitiveInstance {
|
||||
SimplePrimitiveInstance {
|
||||
global_prim_index: prim_index.0 as i32,
|
||||
specific_prim_address: specific_prim_address,
|
||||
task_index: task_index.0 as i32,
|
||||
clip_task_index: clip_task_index.0 as i32,
|
||||
|
@ -1332,6 +1341,7 @@ impl SimplePrimitiveInstance {
|
|||
fn build(&self, data0: i32, data1: i32) -> PrimitiveInstance {
|
||||
PrimitiveInstance {
|
||||
data: [
|
||||
self.global_prim_index,
|
||||
self.specific_prim_address,
|
||||
self.task_index,
|
||||
self.clip_task_index,
|
||||
|
@ -1339,7 +1349,6 @@ impl SimplePrimitiveInstance {
|
|||
self.z_sort_index,
|
||||
data0,
|
||||
data1,
|
||||
0,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -1446,7 +1455,7 @@ pub struct StackingContext {
|
|||
|
||||
/// Offset in the parent reference frame to the origin of this stacking
|
||||
/// context's coordinate system.
|
||||
pub reference_frame_offset: LayerVector2D,
|
||||
pub reference_frame_offset: LayerPoint,
|
||||
|
||||
/// The `ClipId` of the owning reference frame.
|
||||
pub reference_frame_id: ClipId,
|
||||
|
@ -1475,7 +1484,7 @@ pub struct StackingContext {
|
|||
|
||||
impl StackingContext {
|
||||
pub fn new(pipeline_id: PipelineId,
|
||||
reference_frame_offset: LayerVector2D,
|
||||
reference_frame_offset: LayerPoint,
|
||||
is_page_root: bool,
|
||||
reference_frame_id: ClipId,
|
||||
local_bounds: LayerRect,
|
||||
|
@ -1603,8 +1612,9 @@ impl CompositeOps {
|
|||
|
||||
pub fn will_make_invisible(&self) -> bool {
|
||||
for op in &self.filters {
|
||||
if op == &LowLevelFilterOp::Opacity(Au(0)) {
|
||||
return true;
|
||||
match op {
|
||||
&LowLevelFilterOp::Opacity(Au(0)) => return true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
false
|
||||
|
@ -1632,7 +1642,11 @@ pub struct Frame {
|
|||
|
||||
pub layer_texture_data: Vec<PackedLayer>,
|
||||
pub render_task_data: Vec<RenderTaskData>,
|
||||
pub gpu_data16: Vec<GpuBlock16>,
|
||||
pub gpu_data32: Vec<GpuBlock32>,
|
||||
pub gpu_geometry: Vec<PrimitiveGeometry>,
|
||||
pub gpu_gradient_data: Vec<GradientData>,
|
||||
pub gpu_split_geometry: Vec<SplitGeometry>,
|
||||
pub gpu_resource_rects: Vec<TexelRect>,
|
||||
|
||||
// List of updates that need to be pushed to the
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
|
||||
use std::f32::consts::{FRAC_1_SQRT_2};
|
||||
use euclid::{Point2D, Rect, Size2D};
|
||||
use euclid::{TypedRect, TypedPoint2D, TypedSize2D, TypedTransform3D};
|
||||
use euclid::{TypedRect, TypedPoint2D, TypedSize2D, TypedMatrix4D};
|
||||
use webrender_traits::{DeviceIntRect, DeviceIntPoint, DeviceIntSize};
|
||||
use webrender_traits::{LayerRect, WorldPoint3D, LayerToWorldTransform};
|
||||
use webrender_traits::{LayerRect, WorldPoint4D, LayerPoint4D, LayerToWorldTransform};
|
||||
use webrender_traits::{BorderRadius, ComplexClipRegion, LayoutRect};
|
||||
use num_traits::Zero;
|
||||
|
||||
|
@ -20,17 +20,17 @@ pub trait MatrixHelpers<Src, Dst> {
|
|||
fn preserves_2d_axis_alignment(&self) -> bool;
|
||||
}
|
||||
|
||||
impl<Src, Dst> MatrixHelpers<Src, Dst> for TypedTransform3D<f32, Src, Dst> {
|
||||
impl<Src, Dst> MatrixHelpers<Src, Dst> for TypedMatrix4D<f32, Src, Dst> {
|
||||
fn transform_rect(&self, rect: &TypedRect<f32, Src>) -> TypedRect<f32, Dst> {
|
||||
let top_left = self.transform_point2d(&rect.origin);
|
||||
let top_right = self.transform_point2d(&rect.top_right());
|
||||
let bottom_left = self.transform_point2d(&rect.bottom_left());
|
||||
let bottom_right = self.transform_point2d(&rect.bottom_right());
|
||||
let top_left = self.transform_point(&rect.origin);
|
||||
let top_right = self.transform_point(&rect.top_right());
|
||||
let bottom_left = self.transform_point(&rect.bottom_left());
|
||||
let bottom_right = self.transform_point(&rect.bottom_right());
|
||||
TypedRect::from_points(&[top_left, top_right, bottom_right, bottom_left])
|
||||
}
|
||||
|
||||
fn is_identity(&self) -> bool {
|
||||
*self == TypedTransform3D::identity()
|
||||
*self == TypedMatrix4D::identity()
|
||||
}
|
||||
|
||||
// A port of the preserves2dAxisAlignment function in Skia.
|
||||
|
@ -171,7 +171,7 @@ pub struct TransformedRect {
|
|||
pub local_rect: LayerRect,
|
||||
pub bounding_rect: DeviceIntRect,
|
||||
pub inner_rect: DeviceIntRect,
|
||||
pub vertices: [WorldPoint3D; 4],
|
||||
pub vertices: [WorldPoint4D; 4],
|
||||
pub kind: TransformedRectKind,
|
||||
}
|
||||
|
||||
|
@ -221,17 +221,30 @@ impl TransformedRect {
|
|||
TransformedRectKind::Complex => {
|
||||
*/
|
||||
let vertices = [
|
||||
transform.transform_point3d(&rect.origin.to_3d()),
|
||||
transform.transform_point3d(&rect.bottom_left().to_3d()),
|
||||
transform.transform_point3d(&rect.bottom_right().to_3d()),
|
||||
transform.transform_point3d(&rect.top_right().to_3d()),
|
||||
transform.transform_point4d(&LayerPoint4D::new(rect.origin.x,
|
||||
rect.origin.y,
|
||||
0.0,
|
||||
1.0)),
|
||||
transform.transform_point4d(&LayerPoint4D::new(rect.bottom_left().x,
|
||||
rect.bottom_left().y,
|
||||
0.0,
|
||||
1.0)),
|
||||
transform.transform_point4d(&LayerPoint4D::new(rect.bottom_right().x,
|
||||
rect.bottom_right().y,
|
||||
0.0,
|
||||
1.0)),
|
||||
transform.transform_point4d(&LayerPoint4D::new(rect.top_right().x,
|
||||
rect.top_right().y,
|
||||
0.0,
|
||||
1.0)),
|
||||
];
|
||||
|
||||
let (mut xs, mut ys) = ([0.0; 4], [0.0; 4]);
|
||||
|
||||
for (vertex, (x, y)) in vertices.iter().zip(xs.iter_mut().zip(ys.iter_mut())) {
|
||||
*x = get_normal(vertex.x).unwrap_or(0.0);
|
||||
*y = get_normal(vertex.y).unwrap_or(0.0);
|
||||
let inv_w = 1.0 / vertex.w;
|
||||
*x = get_normal(vertex.x * inv_w).unwrap_or(0.0);
|
||||
*y = get_normal(vertex.y * inv_w).unwrap_or(0.0);
|
||||
}
|
||||
|
||||
xs.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
||||
|
|
|
@ -8,7 +8,7 @@ license = "MPL-2.0"
|
|||
webrender_traits = {path = "../webrender_traits", version = "0.40.0"}
|
||||
rayon = {version = "0.7", features = ["unstable"]}
|
||||
thread_profiler = "0.1.1"
|
||||
euclid = "0.14.4"
|
||||
euclid = "0.13"
|
||||
app_units = "0.4"
|
||||
gleam = "0.4"
|
||||
|
||||
|
|
|
@ -13,8 +13,7 @@ use webrender::{ApiRecordingReceiver, BinaryRecorder};
|
|||
use thread_profiler::register_thread_with_profiler;
|
||||
use moz2d_renderer::Moz2dImageRenderer;
|
||||
use app_units::Au;
|
||||
use euclid::{TypedPoint2D, TypedSize2D, TypedRect, TypedTransform3D, SideOffsets2D};
|
||||
use euclid::TypedVector2D;
|
||||
use euclid::{TypedPoint2D, TypedSize2D, TypedRect, TypedMatrix4D, SideOffsets2D};
|
||||
use rayon;
|
||||
|
||||
extern crate webrender_traits;
|
||||
|
@ -178,12 +177,6 @@ impl<U> Into<TypedPoint2D<f32, U>> for WrPoint {
|
|||
}
|
||||
}
|
||||
|
||||
impl<U> Into<TypedVector2D<f32, U>> for WrPoint {
|
||||
fn into(self) -> TypedVector2D<f32, U> {
|
||||
TypedVector2D::new(self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct WrSize {
|
||||
|
@ -239,44 +232,44 @@ pub struct WrMatrix {
|
|||
values: [f32; 16],
|
||||
}
|
||||
|
||||
impl<'a, U, E> Into<TypedTransform3D<f32, U, E>> for &'a WrMatrix {
|
||||
fn into(self) -> TypedTransform3D<f32, U, E> {
|
||||
TypedTransform3D::row_major(self.values[0],
|
||||
self.values[1],
|
||||
self.values[2],
|
||||
self.values[3],
|
||||
self.values[4],
|
||||
self.values[5],
|
||||
self.values[6],
|
||||
self.values[7],
|
||||
self.values[8],
|
||||
self.values[9],
|
||||
self.values[10],
|
||||
self.values[11],
|
||||
self.values[12],
|
||||
self.values[13],
|
||||
self.values[14],
|
||||
self.values[15])
|
||||
impl<'a, U, E> Into<TypedMatrix4D<f32, U, E>> for &'a WrMatrix {
|
||||
fn into(self) -> TypedMatrix4D<f32, U, E> {
|
||||
TypedMatrix4D::row_major(self.values[0],
|
||||
self.values[1],
|
||||
self.values[2],
|
||||
self.values[3],
|
||||
self.values[4],
|
||||
self.values[5],
|
||||
self.values[6],
|
||||
self.values[7],
|
||||
self.values[8],
|
||||
self.values[9],
|
||||
self.values[10],
|
||||
self.values[11],
|
||||
self.values[12],
|
||||
self.values[13],
|
||||
self.values[14],
|
||||
self.values[15])
|
||||
}
|
||||
}
|
||||
impl<U, E> Into<TypedTransform3D<f32, U, E>> for WrMatrix {
|
||||
fn into(self) -> TypedTransform3D<f32, U, E> {
|
||||
TypedTransform3D::row_major(self.values[0],
|
||||
self.values[1],
|
||||
self.values[2],
|
||||
self.values[3],
|
||||
self.values[4],
|
||||
self.values[5],
|
||||
self.values[6],
|
||||
self.values[7],
|
||||
self.values[8],
|
||||
self.values[9],
|
||||
self.values[10],
|
||||
self.values[11],
|
||||
self.values[12],
|
||||
self.values[13],
|
||||
self.values[14],
|
||||
self.values[15])
|
||||
impl<U, E> Into<TypedMatrix4D<f32, U, E>> for WrMatrix {
|
||||
fn into(self) -> TypedMatrix4D<f32, U, E> {
|
||||
TypedMatrix4D::row_major(self.values[0],
|
||||
self.values[1],
|
||||
self.values[2],
|
||||
self.values[3],
|
||||
self.values[4],
|
||||
self.values[5],
|
||||
self.values[6],
|
||||
self.values[7],
|
||||
self.values[8],
|
||||
self.values[9],
|
||||
self.values[10],
|
||||
self.values[11],
|
||||
self.values[12],
|
||||
self.values[13],
|
||||
self.values[14],
|
||||
self.values[15])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,10 +32,10 @@ impl BlobImageRenderer for Moz2dImageRenderer {
|
|||
}
|
||||
|
||||
fn request(&mut self,
|
||||
_resources: &BlobImageResources,
|
||||
request: BlobImageRequest,
|
||||
descriptor: &BlobImageDescriptor,
|
||||
_dirty_rect: Option<DeviceUintRect>) {
|
||||
_dirty_rect: Option<DeviceUintRect>,
|
||||
_images: &ImageStore) {
|
||||
debug_assert!(!self.rendered_images.contains_key(&request));
|
||||
// TODO: implement tiling.
|
||||
assert!(request.tile.is_none());
|
||||
|
@ -105,8 +105,6 @@ impl BlobImageRenderer for Moz2dImageRenderer {
|
|||
// If we break out of the loop above it means the channel closed unexpectedly.
|
||||
Err(BlobImageError::Other("Channel closed".into()))
|
||||
}
|
||||
fn delete_font(&mut self, _font: FontKey) {
|
||||
}
|
||||
}
|
||||
|
||||
impl Moz2dImageRenderer {
|
||||
|
|
|
@ -14,11 +14,11 @@ webgl = ["offscreen_gl_context"]
|
|||
app_units = "0.4"
|
||||
bincode = "1.0.0-alpha2"
|
||||
byteorder = "1.0"
|
||||
euclid = "0.14.4"
|
||||
euclid = "0.13"
|
||||
gleam = "0.4.5"
|
||||
heapsize = ">= 0.3.6, < 0.5"
|
||||
ipc-channel = {version = "0.7.2", optional = true}
|
||||
offscreen_gl_context = {version = "0.9", features = ["serde"], optional = true}
|
||||
offscreen_gl_context = {version = "0.8", features = ["serde"], optional = true}
|
||||
serde = "0.9"
|
||||
serde_derive = "0.9"
|
||||
time = "0.1"
|
||||
|
|
|
@ -10,7 +10,7 @@ use std::fmt;
|
|||
use std::marker::PhantomData;
|
||||
use {BuiltDisplayList, BuiltDisplayListDescriptor, ClipId, ColorF, DeviceIntPoint, DeviceIntSize};
|
||||
use {DeviceUintRect, DeviceUintSize, FontKey, GlyphDimensions, GlyphKey};
|
||||
use {ImageData, ImageDescriptor, ImageKey, LayoutPoint, LayoutVector2D, LayoutSize, LayoutTransform};
|
||||
use {ImageData, ImageDescriptor, ImageKey, LayoutPoint, LayoutSize, LayoutTransform};
|
||||
use {NativeFontHandle, WorldPoint};
|
||||
#[cfg(feature = "webgl")]
|
||||
use {WebGLCommand, WebGLContextId};
|
||||
|
@ -461,13 +461,13 @@ pub enum ScrollEventPhase {
|
|||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub struct ScrollLayerState {
|
||||
pub id: ClipId,
|
||||
pub scroll_offset: LayoutVector2D,
|
||||
pub scroll_offset: LayoutPoint,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
pub enum ScrollLocation {
|
||||
/// Scroll by a certain amount.
|
||||
Delta(LayoutVector2D),
|
||||
Delta(LayoutPoint),
|
||||
/// Scroll to very top of element.
|
||||
Start,
|
||||
/// Scroll to very bottom of element.
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
use app_units::Au;
|
||||
use euclid::SideOffsets2D;
|
||||
use {ColorF, FontKey, ImageKey, ItemRange, PipelineId, WebGLContextId};
|
||||
use {LayoutPoint, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D};
|
||||
use {LayoutPoint, LayoutRect, LayoutSize, LayoutTransform};
|
||||
use {PropertyBinding};
|
||||
|
||||
// NOTE: some of these structs have an "IMPLICIT" comment.
|
||||
|
@ -207,7 +207,7 @@ pub enum BoxShadowClipMode {
|
|||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct BoxShadowDisplayItem {
|
||||
pub box_bounds: LayoutRect,
|
||||
pub offset: LayoutVector2D,
|
||||
pub offset: LayoutPoint,
|
||||
pub color: ColorF,
|
||||
pub blur_radius: f32,
|
||||
pub spread_radius: f32,
|
||||
|
@ -325,23 +325,6 @@ pub enum FilterOp {
|
|||
Sepia(f32),
|
||||
}
|
||||
|
||||
impl FilterOp {
|
||||
pub fn is_noop(&self) -> bool {
|
||||
match *self {
|
||||
FilterOp::Blur(length) if length == Au(0) => true,
|
||||
FilterOp::Brightness(amount) if amount == 1.0 => true,
|
||||
FilterOp::Contrast(amount) if amount == 1.0 => true,
|
||||
FilterOp::Grayscale(amount) if amount == 0.0 => true,
|
||||
FilterOp::HueRotate(amount) if amount == 0.0 => true,
|
||||
FilterOp::Invert(amount) if amount == 0.0 => true,
|
||||
FilterOp::Opacity(amount) if amount == PropertyBinding::Value(1.0) => true,
|
||||
FilterOp::Saturate(amount) if amount == 1.0 => true,
|
||||
FilterOp::Sepia(amount) if amount == 0.0 => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct IframeDisplayItem {
|
||||
pub pipeline_id: PipelineId,
|
||||
|
@ -583,7 +566,7 @@ impl ComplexClipRegion {
|
|||
pub enum ClipId {
|
||||
Clip(u64, PipelineId),
|
||||
ClipExternalId(u64, PipelineId),
|
||||
DynamicallyAddedNode(u64, PipelineId),
|
||||
ReferenceFrame(u64, PipelineId),
|
||||
}
|
||||
|
||||
impl ClipId {
|
||||
|
@ -592,7 +575,7 @@ impl ClipId {
|
|||
}
|
||||
|
||||
pub fn root_reference_frame(pipeline_id: PipelineId) -> ClipId {
|
||||
ClipId::DynamicallyAddedNode(0, pipeline_id)
|
||||
ClipId::ReferenceFrame(0, pipeline_id)
|
||||
}
|
||||
|
||||
pub fn new(id: u64, pipeline_id: PipelineId) -> ClipId {
|
||||
|
@ -609,7 +592,14 @@ impl ClipId {
|
|||
match *self {
|
||||
ClipId::Clip(_, pipeline_id) |
|
||||
ClipId::ClipExternalId(_, pipeline_id) |
|
||||
ClipId::DynamicallyAddedNode(_, pipeline_id) => pipeline_id,
|
||||
ClipId::ReferenceFrame(_, pipeline_id) => pipeline_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_reference_frame(&self) -> bool {
|
||||
match *self {
|
||||
ClipId::ReferenceFrame(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ use {ImageRendering, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform, MixBl
|
|||
use {PipelineId, PropertyBinding, PushStackingContextDisplayItem, RadialGradient};
|
||||
use {RadialGradientDisplayItem, RectangleDisplayItem, ScrollPolicy, SpecificDisplayItem};
|
||||
use {StackingContext, TextDisplayItem, TransformStyle, WebGLContextId, WebGLDisplayItem};
|
||||
use {YuvColorSpace, YuvData, YuvImageDisplayItem, LayoutVector2D};
|
||||
use {YuvColorSpace, YuvData, YuvImageDisplayItem};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[repr(C)]
|
||||
|
@ -773,7 +773,7 @@ impl DisplayListBuilder {
|
|||
rect: LayoutRect,
|
||||
_token: ClipRegionToken,
|
||||
box_bounds: LayoutRect,
|
||||
offset: LayoutVector2D,
|
||||
offset: LayoutPoint,
|
||||
color: ColorF,
|
||||
blur_radius: f32,
|
||||
spread_radius: f32,
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
use app_units::Au;
|
||||
use {ColorU, ColorF, LayoutPoint};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(target_os = "macos")] use core_foundation::string::CFString;
|
||||
#[cfg(target_os = "macos")] use core_graphics::font::CGFont;
|
||||
|
@ -64,13 +63,6 @@ impl FontKey {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum FontTemplate {
|
||||
Raw(Arc<Vec<u8>>, u32),
|
||||
Native(NativeFontHandle),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Ord, PartialOrd)]
|
||||
pub enum FontRenderMode {
|
||||
Mono,
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
use std::sync::Arc;
|
||||
use {DeviceUintRect, DevicePoint};
|
||||
use {TileOffset, TileSize};
|
||||
use font::{FontKey, FontTemplate};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
|
@ -136,11 +135,6 @@ impl ImageData {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait BlobImageResources {
|
||||
fn get_font_data(&self, key: FontKey) -> &FontTemplate;
|
||||
fn get_image(&self, key: ImageKey) -> Option<(&ImageData, &ImageDescriptor)>;
|
||||
}
|
||||
|
||||
pub trait BlobImageRenderer: Send {
|
||||
fn add(&mut self, key: ImageKey, data: BlobImageData, tiling: Option<TileSize>);
|
||||
|
||||
|
@ -149,14 +143,12 @@ pub trait BlobImageRenderer: Send {
|
|||
fn delete(&mut self, key: ImageKey);
|
||||
|
||||
fn request(&mut self,
|
||||
services: &BlobImageResources,
|
||||
key: BlobImageRequest,
|
||||
descriptor: &BlobImageDescriptor,
|
||||
dirty_rect: Option<DeviceUintRect>);
|
||||
dirty_rect: Option<DeviceUintRect>,
|
||||
images: &ImageStore);
|
||||
|
||||
fn resolve(&mut self, key: BlobImageRequest) -> BlobImageResult;
|
||||
|
||||
fn delete_font(&mut self, key: FontKey);
|
||||
}
|
||||
|
||||
pub type BlobImageData = Vec<u8>;
|
||||
|
@ -191,3 +183,7 @@ pub struct BlobImageRequest {
|
|||
pub key: ImageKey,
|
||||
pub tile: Option<TileOffset>,
|
||||
}
|
||||
|
||||
pub trait ImageStore {
|
||||
fn get_image(&self, key: ImageKey) -> Option<(&ImageData, &ImageDescriptor)>;
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
//! The terms "layer" and "stacking context" can be used interchangeably
|
||||
//! in the context of coordinate systems.
|
||||
|
||||
use euclid::{Length, TypedTransform3D, TypedRect, TypedSize2D};
|
||||
use euclid::{TypedPoint2D, TypedPoint3D, TypedVector2D, TypedVector3D};
|
||||
use euclid::{Length, TypedMatrix4D, TypedRect, TypedSize2D};
|
||||
use euclid::{TypedPoint2D, TypedPoint3D, TypedPoint4D};
|
||||
|
||||
/// Geometry in the coordinate system of the render target (screen or intermediate
|
||||
/// surface) in physical pixels.
|
||||
|
@ -31,7 +31,6 @@ pub type DeviceUintSize = TypedSize2D<u32, DevicePixel>;
|
|||
|
||||
pub type DeviceRect = TypedRect<f32, DevicePixel>;
|
||||
pub type DevicePoint = TypedPoint2D<f32, DevicePixel>;
|
||||
pub type DeviceVector2D = TypedVector2D<f32, DevicePixel>;
|
||||
pub type DeviceSize = TypedSize2D<f32, DevicePixel>;
|
||||
|
||||
/// Geometry in a stacking context's local coordinate space (logical pixels).
|
||||
|
@ -41,8 +40,6 @@ pub type LayoutPixel = LayerPixel;
|
|||
|
||||
pub type LayoutRect = LayerRect;
|
||||
pub type LayoutPoint = LayerPoint;
|
||||
pub type LayoutVector2D = LayerVector2D;
|
||||
pub type LayoutVector3D = LayerVector3D;
|
||||
pub type LayoutSize = LayerSize;
|
||||
|
||||
/// Geometry in a layer's local coordinate space (logical pixels).
|
||||
|
@ -51,10 +48,8 @@ pub struct LayerPixel;
|
|||
|
||||
pub type LayerRect = TypedRect<f32, LayerPixel>;
|
||||
pub type LayerPoint = TypedPoint2D<f32, LayerPixel>;
|
||||
pub type LayerPoint3D = TypedPoint3D<f32, LayerPixel>;
|
||||
pub type LayerVector2D = TypedVector2D<f32, LayerPixel>;
|
||||
pub type LayerVector3D = TypedVector3D<f32, LayerPixel>;
|
||||
pub type LayerSize = TypedSize2D<f32, LayerPixel>;
|
||||
pub type LayerPoint4D = TypedPoint4D<f32, LayerPixel>;
|
||||
|
||||
/// Geometry in a layer's scrollable parent coordinate space (logical pixels).
|
||||
///
|
||||
|
@ -68,7 +63,6 @@ pub struct ScrollLayerPixel;
|
|||
|
||||
pub type ScrollLayerRect = TypedRect<f32, ScrollLayerPixel>;
|
||||
pub type ScrollLayerPoint = TypedPoint2D<f32, ScrollLayerPixel>;
|
||||
pub type ScrollLayerVector2D = TypedVector2D<f32, ScrollLayerPixel>;
|
||||
pub type ScrollLayerSize = TypedSize2D<f32, ScrollLayerPixel>;
|
||||
|
||||
/// Geometry in the document's coordinate space (logical pixels).
|
||||
|
@ -79,21 +73,20 @@ pub type WorldRect = TypedRect<f32, WorldPixel>;
|
|||
pub type WorldPoint = TypedPoint2D<f32, WorldPixel>;
|
||||
pub type WorldSize = TypedSize2D<f32, WorldPixel>;
|
||||
pub type WorldPoint3D = TypedPoint3D<f32, WorldPixel>;
|
||||
pub type WorldVector2D = TypedVector2D<f32, WorldPixel>;
|
||||
pub type WorldVector3D = TypedVector3D<f32, WorldPixel>;
|
||||
pub type WorldPoint4D = TypedPoint4D<f32, WorldPixel>;
|
||||
|
||||
/// Offset in number of tiles.
|
||||
#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub struct Tiles;
|
||||
pub type TileOffset = TypedPoint2D<u16, Tiles>;
|
||||
|
||||
pub type LayoutTransform = TypedTransform3D<f32, LayoutPixel, LayoutPixel>;
|
||||
pub type LayerTransform = TypedTransform3D<f32, LayerPixel, LayerPixel>;
|
||||
pub type LayerToScrollTransform = TypedTransform3D<f32, LayerPixel, ScrollLayerPixel>;
|
||||
pub type ScrollToLayerTransform = TypedTransform3D<f32, ScrollLayerPixel, LayerPixel>;
|
||||
pub type LayerToWorldTransform = TypedTransform3D<f32, LayerPixel, WorldPixel>;
|
||||
pub type WorldToLayerTransform = TypedTransform3D<f32, WorldPixel, LayerPixel>;
|
||||
pub type ScrollToWorldTransform = TypedTransform3D<f32, ScrollLayerPixel, WorldPixel>;
|
||||
pub type LayoutTransform = TypedMatrix4D<f32, LayoutPixel, LayoutPixel>;
|
||||
pub type LayerTransform = TypedMatrix4D<f32, LayerPixel, LayerPixel>;
|
||||
pub type LayerToScrollTransform = TypedMatrix4D<f32, LayerPixel, ScrollLayerPixel>;
|
||||
pub type ScrollToLayerTransform = TypedMatrix4D<f32, ScrollLayerPixel, LayerPixel>;
|
||||
pub type LayerToWorldTransform = TypedMatrix4D<f32, LayerPixel, WorldPixel>;
|
||||
pub type WorldToLayerTransform = TypedMatrix4D<f32, WorldPixel, LayerPixel>;
|
||||
pub type ScrollToWorldTransform = TypedMatrix4D<f32, ScrollLayerPixel, WorldPixel>;
|
||||
|
||||
|
||||
pub fn device_length(value: f32, device_pixel_ratio: f32) -> DeviceIntLength {
|
||||
|
@ -104,7 +97,3 @@ pub fn as_scroll_parent_rect(rect: &LayerRect) -> ScrollLayerRect {
|
|||
ScrollLayerRect::from_untyped(&rect.to_untyped())
|
||||
}
|
||||
|
||||
pub fn as_scroll_parent_vector(vector: &LayerVector2D) -> ScrollLayerVector2D {
|
||||
ScrollLayerVector2D::from_untyped(&vector.to_untyped())
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"118514fd9c4958df0d25584cda4917186c46011569f55ef350530c1ad3fbdb48",".travis.yml":"13d3e5a7bf83b04c8e8cfa14f0297bd8366d68391d977dd547f64707dffc275a","COPYRIGHT":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"c91c98dc9510ef29a7ce0d7c78294f15cb139c9afecca38e5fda56b0a6984954","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"625bec69c76ce5423fdd05cfe46922b2680ec517f97c5854ce34798d1d8a9541","src/approxeq.rs":"6cf810ad389c73a27141a7a67454ed12d4b01c3c16605b9a7414b389bc0615dd","src/length.rs":"d7c6369f2fe2a17c845b57749bd48c471159f0571a7314d3bf90737d53f697d3","src/lib.rs":"e2e621f05304278d020429d0349acf7a4e7c7a9a72bd23fc0e55680267472ee9","src/macros.rs":"b63dabdb52df84ea170dc1dab5fe8d7a78c054562d1566bab416124708d2d7af","src/matrix2d.rs":"2361338f59813adf4eebaab76e4dd82be0fbfb9ff2461da8dd9ac9d43583b322","src/matrix4d.rs":"b8547bed6108b037192021c97169c00ad456120b849e9b7ac7bec40363edaec1","src/num.rs":"749b201289fc6663199160a2f9204e17925fd3053f8ab7779e7bfb377ad06227","src/point.rs":"dbf12a3ad35dc2502b7f2637856d8ee40f5a96e37ed00f3ee3272bf5752c060c","src/rect.rs":"0a255046dd11a6093d9a77e327e1df31802808536b4d87e4e3b80ff6b208de0f","src/scale_factor.rs":"df6dbd1f0f9f63210b92809f84a383dad982a74f09789cf22c7d8f9b62199d39","src/side_offsets.rs":"f85526a421ffda63ff01a3478d4162c8717eef68e942acfa2fd9a1adee02ebb2","src/size.rs":"ae1b647e300600b50a21dba8c1d915801782ebae82baeb5e49017e6d68a49b28","src/trig.rs":"ef290927af252ca90a29ba9f17158b591ed591604e66cb9df045dd47b9cfdca5"},"package":"6083f113c422ff9cd855a1cf6cc8ec0903606c0eb43a0c6a0ced3bdc9731e4c1"}
|
|
@ -1,2 +0,0 @@
|
|||
Cargo.lock
|
||||
/target/
|
|
@ -1,19 +0,0 @@
|
|||
language: rust
|
||||
|
||||
notifications:
|
||||
webhooks: http://build.servo.org:54856/travis
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- rust: stable
|
||||
env: FEATURES=""
|
||||
- rust: beta
|
||||
env: FEATURES=""
|
||||
- rust: nightly
|
||||
env: FEATURES=""
|
||||
- rust: nightly
|
||||
env: FEATURES="unstable"
|
||||
|
||||
script:
|
||||
- cargo build --verbose --features "$FEATURES"
|
||||
- cargo test --verbose --features "$FEATURES"
|
|
@ -1,5 +0,0 @@
|
|||
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
<LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
option. All files in the project carrying such notice may not be
|
||||
copied, modified, or distributed except according to those terms.
|
|
@ -1,24 +0,0 @@
|
|||
[package]
|
||||
name = "euclid"
|
||||
version = "0.13.0"
|
||||
authors = ["The Servo Project Developers"]
|
||||
description = "Geometry primitives"
|
||||
documentation = "https://docs.rs/euclid/"
|
||||
repository = "https://github.com/servo/euclid"
|
||||
keywords = ["matrix", "vector", "linear-algebra", "geometry"]
|
||||
categories = ["science"]
|
||||
license = "MIT / Apache-2.0"
|
||||
|
||||
[features]
|
||||
unstable = []
|
||||
|
||||
[dependencies]
|
||||
heapsize = "0.4"
|
||||
rustc-serialize = "0.3.2"
|
||||
num-traits = {version = "0.1.32", default-features = false}
|
||||
log = "0.3.1"
|
||||
serde = "0.9"
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.3.7"
|
||||
serde_test = "0.9"
|
|
@ -1,201 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -1,25 +0,0 @@
|
|||
Copyright (c) 2012-2013 Mozilla Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
|
@ -1,8 +0,0 @@
|
|||
# euclid
|
||||
|
||||
This is a small library for geometric types with a focus on 2d graphics and
|
||||
layout.
|
||||
|
||||
* [Documentation](https://docs.rs/euclid/)
|
||||
* [Release notes](https://github.com/servo/euclid/releases)
|
||||
* [crates.io](https://crates.io/crates/euclid)
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
|
||||
/// Trait for testing approximate equality
|
||||
pub trait ApproxEq<Eps> {
|
||||
fn approx_epsilon() -> Eps;
|
||||
fn approx_eq(&self, other: &Self) -> bool;
|
||||
fn approx_eq_eps(&self, other: &Self, approx_epsilon: &Eps) -> bool;
|
||||
}
|
||||
|
||||
macro_rules! approx_eq {
|
||||
($ty:ty, $eps:expr) => (
|
||||
impl ApproxEq<$ty> for $ty {
|
||||
#[inline]
|
||||
fn approx_epsilon() -> $ty { $eps }
|
||||
#[inline]
|
||||
fn approx_eq(&self, other: &$ty) -> bool {
|
||||
self.approx_eq_eps(other, &$eps)
|
||||
}
|
||||
#[inline]
|
||||
fn approx_eq_eps(&self, other: &$ty, approx_epsilon: &$ty) -> bool {
|
||||
(*self - *other).abs() < *approx_epsilon
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
approx_eq!(f32, 1.0e-6);
|
||||
approx_eq!(f64, 1.0e-6);
|
|
@ -1,449 +0,0 @@
|
|||
// Copyright 2014 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//! A one-dimensional length, tagged with its units.
|
||||
|
||||
use scale_factor::ScaleFactor;
|
||||
use num::Zero;
|
||||
|
||||
use heapsize::HeapSizeOf;
|
||||
use num_traits::{NumCast, Saturating};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::cmp::Ordering;
|
||||
use std::ops::{Add, Sub, Mul, Div, Neg};
|
||||
use std::ops::{AddAssign, SubAssign};
|
||||
use std::marker::PhantomData;
|
||||
use std::fmt;
|
||||
|
||||
/// A one-dimensional distance, with value represented by `T` and unit of measurement `Unit`.
|
||||
///
|
||||
/// `T` can be any numeric type, for example a primitive type like `u64` or `f32`.
|
||||
///
|
||||
/// `Unit` is not used in the representation of a `Length` value. It is used only at compile time
|
||||
/// to ensure that a `Length` stored with one unit is converted explicitly before being used in an
|
||||
/// expression that requires a different unit. It may be a type without values, such as an empty
|
||||
/// enum.
|
||||
///
|
||||
/// You can multiply a `Length` by a `scale_factor::ScaleFactor` to convert it from one unit to
|
||||
/// another. See the `ScaleFactor` docs for an example.
|
||||
// Uncomment the derive, and remove the macro call, once heapsize gets
|
||||
// PhantomData<T> support.
|
||||
#[repr(C)]
|
||||
#[derive(RustcDecodable, RustcEncodable)]
|
||||
pub struct Length<T, Unit>(pub T, PhantomData<Unit>);
|
||||
|
||||
impl<T: Clone, Unit> Clone for Length<T, Unit> {
|
||||
fn clone(&self) -> Self {
|
||||
Length(self.0.clone(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, Unit> Copy for Length<T, Unit> {}
|
||||
|
||||
impl<Unit, T: HeapSizeOf> HeapSizeOf for Length<T, Unit> {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.0.heap_size_of_children()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Unit, T> Deserialize for Length<T, Unit> where T: Deserialize {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Length<T, Unit>,D::Error>
|
||||
where D: Deserializer {
|
||||
Ok(Length(try!(Deserialize::deserialize(deserializer)), PhantomData))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Unit> Serialize for Length<T, Unit> where T: Serialize {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
|
||||
self.0.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Unit> Length<T, Unit> {
|
||||
pub fn new(x: T) -> Length<T, Unit> {
|
||||
Length(x, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Unit, T: Clone> Length<T, Unit> {
|
||||
pub fn get(&self) -> T {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug + Clone, U> fmt::Debug for Length<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.get().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display + Clone, U> fmt::Display for Length<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.get().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
// length + length
|
||||
impl<U, T: Clone + Add<T, Output=T>> Add for Length<T, U> {
|
||||
type Output = Length<T, U>;
|
||||
fn add(self, other: Length<T, U>) -> Length<T, U> {
|
||||
Length::new(self.get() + other.get())
|
||||
}
|
||||
}
|
||||
|
||||
// length += length
|
||||
impl<U, T: Clone + AddAssign<T>> AddAssign for Length<T, U> {
|
||||
fn add_assign(&mut self, other: Length<T, U>) {
|
||||
self.0 += other.get();
|
||||
}
|
||||
}
|
||||
|
||||
// length - length
|
||||
impl<U, T: Clone + Sub<T, Output=T>> Sub<Length<T, U>> for Length<T, U> {
|
||||
type Output = Length<T, U>;
|
||||
fn sub(self, other: Length<T, U>) -> <Self as Sub>::Output {
|
||||
Length::new(self.get() - other.get())
|
||||
}
|
||||
}
|
||||
|
||||
// length -= length
|
||||
impl<U, T: Clone + SubAssign<T>> SubAssign for Length<T, U> {
|
||||
fn sub_assign(&mut self, other: Length<T, U>) {
|
||||
self.0 -= other.get();
|
||||
}
|
||||
}
|
||||
|
||||
// Saturating length + length and length - length.
|
||||
impl<U, T: Clone + Saturating> Saturating for Length<T, U> {
|
||||
fn saturating_add(self, other: Length<T, U>) -> Length<T, U> {
|
||||
Length::new(self.get().saturating_add(other.get()))
|
||||
}
|
||||
|
||||
fn saturating_sub(self, other: Length<T, U>) -> Length<T, U> {
|
||||
Length::new(self.get().saturating_sub(other.get()))
|
||||
}
|
||||
}
|
||||
|
||||
// length / length
|
||||
impl<Src, Dst, T: Clone + Div<T, Output=T>> Div<Length<T, Src>> for Length<T, Dst> {
|
||||
type Output = ScaleFactor<T, Src, Dst>;
|
||||
#[inline]
|
||||
fn div(self, other: Length<T, Src>) -> ScaleFactor<T, Src, Dst> {
|
||||
ScaleFactor::new(self.get() / other.get())
|
||||
}
|
||||
}
|
||||
|
||||
// length * scaleFactor
|
||||
impl<Src, Dst, T: Clone + Mul<T, Output=T>> Mul<ScaleFactor<T, Src, Dst>> for Length<T, Src> {
|
||||
type Output = Length<T, Dst>;
|
||||
#[inline]
|
||||
fn mul(self, scale: ScaleFactor<T, Src, Dst>) -> Length<T, Dst> {
|
||||
Length::new(self.get() * scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
// length / scaleFactor
|
||||
impl<Src, Dst, T: Clone + Div<T, Output=T>> Div<ScaleFactor<T, Src, Dst>> for Length<T, Dst> {
|
||||
type Output = Length<T, Src>;
|
||||
#[inline]
|
||||
fn div(self, scale: ScaleFactor<T, Src, Dst>) -> Length<T, Src> {
|
||||
Length::new(self.get() / scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
// -length
|
||||
impl <U, T:Clone + Neg<Output=T>> Neg for Length<T, U> {
|
||||
type Output = Length<T, U>;
|
||||
#[inline]
|
||||
fn neg(self) -> Length<T, U> {
|
||||
Length::new(-self.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Unit, T0: NumCast + Clone> Length<T0, Unit> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
pub fn cast<T1: NumCast + Clone>(&self) -> Option<Length<T1, Unit>> {
|
||||
NumCast::from(self.get()).map(Length::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Unit, T: Clone + PartialEq> PartialEq for Length<T, Unit> {
|
||||
fn eq(&self, other: &Length<T, Unit>) -> bool { self.get().eq(&other.get()) }
|
||||
}
|
||||
|
||||
impl<Unit, T: Clone + PartialOrd> PartialOrd for Length<T, Unit> {
|
||||
fn partial_cmp(&self, other: &Length<T, Unit>) -> Option<Ordering> {
|
||||
self.get().partial_cmp(&other.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Unit, T: Clone + Eq> Eq for Length<T, Unit> {}
|
||||
|
||||
impl<Unit, T: Clone + Ord> Ord for Length<T, Unit> {
|
||||
fn cmp(&self, other: &Length<T, Unit>) -> Ordering { self.get().cmp(&other.get()) }
|
||||
}
|
||||
|
||||
impl<Unit, T: Zero> Zero for Length<T, Unit> {
|
||||
fn zero() -> Length<T, Unit> {
|
||||
Length::new(Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Length;
|
||||
use num::Zero;
|
||||
|
||||
use heapsize::HeapSizeOf;
|
||||
use num_traits::Saturating;
|
||||
use scale_factor::ScaleFactor;
|
||||
use std::f32::INFINITY;
|
||||
|
||||
extern crate serde_test;
|
||||
use self::serde_test::Token;
|
||||
use self::serde_test::assert_tokens;
|
||||
|
||||
enum Inch {}
|
||||
enum Mm {}
|
||||
enum Cm {}
|
||||
enum Second {}
|
||||
|
||||
#[test]
|
||||
fn test_clone() {
|
||||
// A cloned Length is a separate length with the state matching the
|
||||
// original Length at the point it was cloned.
|
||||
let mut variable_length: Length<f32, Inch> = Length::new(12.0);
|
||||
|
||||
let one_foot = variable_length.clone();
|
||||
variable_length.0 = 24.0;
|
||||
|
||||
assert_eq!(one_foot.get(), 12.0);
|
||||
assert_eq!(variable_length.get(), 24.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_heapsizeof_builtins() {
|
||||
// Heap size of built-ins is zero by default.
|
||||
let one_foot: Length<f32, Inch> = Length::new(12.0);
|
||||
|
||||
let heap_size_length_f32 = one_foot.heap_size_of_children();
|
||||
|
||||
assert_eq!(heap_size_length_f32, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_heapsizeof_length_vector() {
|
||||
// Heap size of any Length is just the heap size of the length value.
|
||||
for n in 0..5 {
|
||||
let length: Length<Vec<f32>, Inch> = Length::new(Vec::with_capacity(n));
|
||||
|
||||
assert_eq!(length.heap_size_of_children(), length.0.heap_size_of_children());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_length_serde() {
|
||||
let one_cm: Length<f32, Mm> = Length::new(10.0);
|
||||
|
||||
assert_tokens(&one_cm, &[Token::F32(10.0)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_clones_length_value() {
|
||||
// Calling get returns a clone of the Length's value.
|
||||
// To test this, we need something clone-able - hence a vector.
|
||||
let mut length: Length<Vec<i32>, Inch> = Length::new(vec![1, 2, 3]);
|
||||
|
||||
let value = length.get();
|
||||
length.0.push(4);
|
||||
|
||||
assert_eq!(value, vec![1, 2, 3]);
|
||||
assert_eq!(length.get(), vec![1, 2, 3, 4]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fmt_debug() {
|
||||
// Debug and display format the value only.
|
||||
let one_cm: Length<f32, Mm> = Length::new(10.0);
|
||||
|
||||
let result = format!("{:?}", one_cm);
|
||||
|
||||
assert_eq!(result, "10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fmt_display() {
|
||||
// Debug and display format the value only.
|
||||
let one_cm: Length<f32, Mm> = Length::new(10.0);
|
||||
|
||||
let result = format!("{}", one_cm);
|
||||
|
||||
assert_eq!(result, "10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
let length1: Length<u8, Mm> = Length::new(250);
|
||||
let length2: Length<u8, Mm> = Length::new(5);
|
||||
|
||||
let result = length1 + length2;
|
||||
|
||||
assert_eq!(result.get(), 255);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_addassign() {
|
||||
let one_cm: Length<f32, Mm> = Length::new(10.0);
|
||||
let mut measurement: Length<f32, Mm> = Length::new(5.0);
|
||||
|
||||
measurement += one_cm;
|
||||
|
||||
assert_eq!(measurement.get(), 15.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sub() {
|
||||
let length1: Length<u8, Mm> = Length::new(250);
|
||||
let length2: Length<u8, Mm> = Length::new(5);
|
||||
|
||||
let result = length1 - length2;
|
||||
|
||||
assert_eq!(result.get(), 245);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subassign() {
|
||||
let one_cm: Length<f32, Mm> = Length::new(10.0);
|
||||
let mut measurement: Length<f32, Mm> = Length::new(5.0);
|
||||
|
||||
measurement -= one_cm;
|
||||
|
||||
assert_eq!(measurement.get(), -5.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_saturating_add() {
|
||||
let length1: Length<u8, Mm> = Length::new(250);
|
||||
let length2: Length<u8, Mm> = Length::new(6);
|
||||
|
||||
let result = length1.saturating_add(length2);
|
||||
|
||||
assert_eq!(result.get(), 255);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_saturating_sub() {
|
||||
let length1: Length<u8, Mm> = Length::new(5);
|
||||
let length2: Length<u8, Mm> = Length::new(10);
|
||||
|
||||
let result = length1.saturating_sub(length2);
|
||||
|
||||
assert_eq!(result.get(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_division_by_length() {
|
||||
// Division results in a ScaleFactor from denominator units
|
||||
// to numerator units.
|
||||
let length: Length<f32, Cm> = Length::new(5.0);
|
||||
let duration: Length<f32, Second> = Length::new(10.0);
|
||||
|
||||
let result = length / duration;
|
||||
|
||||
let expected: ScaleFactor<f32, Second, Cm> = ScaleFactor::new(0.5);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiplication() {
|
||||
let length_mm: Length<f32, Mm> = Length::new(10.0);
|
||||
let cm_per_mm: ScaleFactor<f32, Mm, Cm> = ScaleFactor::new(0.1);
|
||||
|
||||
let result = length_mm * cm_per_mm;
|
||||
|
||||
let expected: Length<f32, Cm> = Length::new(1.0);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_division_by_scalefactor() {
|
||||
let length: Length<f32, Cm> = Length::new(5.0);
|
||||
let cm_per_second: ScaleFactor<f32, Second, Cm> = ScaleFactor::new(10.0);
|
||||
|
||||
let result = length / cm_per_second;
|
||||
|
||||
let expected: Length<f32, Second> = Length::new(0.5);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negation() {
|
||||
let length: Length<f32, Cm> = Length::new(5.0);
|
||||
|
||||
let result = -length;
|
||||
|
||||
let expected: Length<f32, Cm> = Length::new(-5.0);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cast() {
|
||||
let length_as_i32: Length<i32, Cm> = Length::new(5);
|
||||
|
||||
let result: Length<f32, Cm> = length_as_i32.cast().unwrap();
|
||||
|
||||
let length_as_f32: Length<f32, Cm> = Length::new(5.0);
|
||||
assert_eq!(result, length_as_f32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_equality() {
|
||||
let length_5_point_0: Length<f32, Cm> = Length::new(5.0);
|
||||
let length_5_point_1: Length<f32, Cm> = Length::new(5.1);
|
||||
let length_0_point_1: Length<f32, Cm> = Length::new(0.1);
|
||||
|
||||
assert!(length_5_point_0 == length_5_point_1 - length_0_point_1);
|
||||
assert!(length_5_point_0 != length_5_point_1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_order() {
|
||||
let length_5_point_0: Length<f32, Cm> = Length::new(5.0);
|
||||
let length_5_point_1: Length<f32, Cm> = Length::new(5.1);
|
||||
let length_0_point_1: Length<f32, Cm> = Length::new(0.1);
|
||||
|
||||
assert!(length_5_point_0 < length_5_point_1);
|
||||
assert!(length_5_point_0 <= length_5_point_1);
|
||||
assert!(length_5_point_0 <= length_5_point_1 - length_0_point_1);
|
||||
assert!(length_5_point_1 > length_5_point_0);
|
||||
assert!(length_5_point_1 >= length_5_point_0);
|
||||
assert!(length_5_point_0 >= length_5_point_1 - length_0_point_1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_add() {
|
||||
type LengthCm = Length<f32, Cm>;
|
||||
let length: LengthCm = Length::new(5.0);
|
||||
|
||||
let result = length - LengthCm::zero();
|
||||
|
||||
assert_eq!(result, length);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_division() {
|
||||
type LengthCm = Length<f32, Cm>;
|
||||
let length: LengthCm = Length::new(5.0);
|
||||
let length_zero: LengthCm = Length::zero();
|
||||
|
||||
let result = length / length_zero;
|
||||
|
||||
let expected: ScaleFactor<f32, Cm, Cm> = ScaleFactor::new(INFINITY);
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![cfg_attr(feature = "unstable", feature(asm, repr_simd, test))]
|
||||
|
||||
//! A collection of strongly typed math tools for computer graphics with an inclination
|
||||
//! towards 2d graphics and layout.
|
||||
//!
|
||||
//! All types are generic over the scalar type of their component (`f32`, `i32`, etc.),
|
||||
//! and tagged with a generic Unit parameter which is useful to prevent mixing
|
||||
//! values from different spaces. For example it should not be legal to translate
|
||||
//! a screen-space position by a world-space vector and this can be expressed using
|
||||
//! the generic Unit parameter.
|
||||
//!
|
||||
//! This unit system is not mandatory and all Typed* structures have an alias
|
||||
//! with the default unit: `UnknownUnit`.
|
||||
//! for example ```Point2D<T>``` is equivalent to ```TypedPoint2D<T, UnknownUnit>```.
|
||||
//! Client code typically creates a set of aliases for each type and doesn't need
|
||||
//! to deal with the specifics of typed units further. For example:
|
||||
//!
|
||||
//! All euclid types are marked `#[repr(C)]` in order to facilitate exposing them to
|
||||
//! foreign function interfaces (provided the underlying scalar type is also `repr(C)`).
|
||||
//!
|
||||
//! ```rust
|
||||
//! use euclid::*;
|
||||
//! pub struct ScreenSpace;
|
||||
//! pub type ScreenPoint = TypedPoint2D<f32, ScreenSpace>;
|
||||
//! pub type ScreenSize = TypedSize2D<f32, ScreenSpace>;
|
||||
//! pub struct WorldSpace;
|
||||
//! pub type WorldPoint = TypedPoint3D<f32, WorldSpace>;
|
||||
//! pub type ProjectionMatrix = TypedMatrix4D<f32, WorldSpace, ScreenSpace>;
|
||||
//! // etc...
|
||||
//! ```
|
||||
//!
|
||||
//! Components are accessed in their scalar form by default for convenience, and most
|
||||
//! types additionally implement strongly typed accessors which return typed ```Length``` wrappers.
|
||||
//! For example:
|
||||
//!
|
||||
//! ```rust
|
||||
//! # use euclid::*;
|
||||
//! # pub struct WorldSpace;
|
||||
//! # pub type WorldPoint = TypedPoint3D<f32, WorldSpace>;
|
||||
//! let p = WorldPoint::new(0.0, 1.0, 1.0);
|
||||
//! // p.x is an f32.
|
||||
//! println!("p.x = {:?} ", p.x);
|
||||
//! // p.x is a Length<f32, WorldSpace>.
|
||||
//! println!("p.x_typed() = {:?} ", p.x_typed());
|
||||
//! // Length::get returns the scalar value (f32).
|
||||
//! assert_eq!(p.x, p.x_typed().get());
|
||||
//! ```
|
||||
|
||||
extern crate heapsize;
|
||||
|
||||
#[cfg_attr(test, macro_use)]
|
||||
extern crate log;
|
||||
extern crate rustc_serialize;
|
||||
extern crate serde;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate rand;
|
||||
#[cfg(feature = "unstable")]
|
||||
extern crate test;
|
||||
extern crate num_traits;
|
||||
|
||||
pub use length::Length;
|
||||
pub use scale_factor::ScaleFactor;
|
||||
pub use matrix2d::{Matrix2D, TypedMatrix2D};
|
||||
pub use matrix4d::{Matrix4D, TypedMatrix4D};
|
||||
pub use point::{
|
||||
Point2D, TypedPoint2D,
|
||||
Point3D, TypedPoint3D,
|
||||
Point4D, TypedPoint4D,
|
||||
};
|
||||
pub use rect::{Rect, TypedRect};
|
||||
pub use side_offsets::{SideOffsets2D, TypedSideOffsets2D};
|
||||
#[cfg(feature = "unstable")] pub use side_offsets::SideOffsets2DSimdI32;
|
||||
pub use size::{Size2D, TypedSize2D};
|
||||
|
||||
pub mod approxeq;
|
||||
pub mod length;
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
pub mod matrix2d;
|
||||
pub mod matrix4d;
|
||||
pub mod num;
|
||||
pub mod point;
|
||||
pub mod rect;
|
||||
pub mod scale_factor;
|
||||
pub mod side_offsets;
|
||||
pub mod size;
|
||||
pub mod trig;
|
||||
|
||||
/// The default unit.
|
||||
#[derive(Clone, Copy, RustcDecodable, RustcEncodable)]
|
||||
pub struct UnknownUnit;
|
||||
|
||||
/// Unit for angles in radians.
|
||||
pub struct Rad;
|
||||
|
||||
/// Unit for angles in degrees.
|
||||
pub struct Deg;
|
||||
|
||||
/// A value in radians.
|
||||
pub type Radians<T> = Length<T, Rad>;
|
||||
|
||||
/// A value in Degrees.
|
||||
pub type Degrees<T> = Length<T, Deg>;
|
|
@ -1,87 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
macro_rules! define_matrix {
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
pub struct $name:ident<T, $($phantom:ident),+> {
|
||||
$(pub $field:ident: T,)+
|
||||
}
|
||||
) => (
|
||||
#[repr(C)]
|
||||
$(#[$attr])*
|
||||
pub struct $name<T, $($phantom),+> {
|
||||
$(pub $field: T,)+
|
||||
_unit: PhantomData<($($phantom),+)>
|
||||
}
|
||||
|
||||
impl<T: Clone, $($phantom),+> Clone for $name<T, $($phantom),+> {
|
||||
fn clone(&self) -> Self {
|
||||
$name {
|
||||
$($field: self.$field.clone(),)+
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, $($phantom),+> Copy for $name<T, $($phantom),+> {}
|
||||
|
||||
impl<T, $($phantom),+> ::heapsize::HeapSizeOf for $name<T, $($phantom),+>
|
||||
where T: ::heapsize::HeapSizeOf
|
||||
{
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
$(self.$field.heap_size_of_children() +)+ 0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, $($phantom),+> ::serde::Deserialize for $name<T, $($phantom),+>
|
||||
where T: ::serde::Deserialize
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where D: ::serde::Deserializer
|
||||
{
|
||||
let ($($field,)+) =
|
||||
try!(::serde::Deserialize::deserialize(deserializer));
|
||||
Ok($name {
|
||||
$($field: $field,)+
|
||||
_unit: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, $($phantom),+> ::serde::Serialize for $name<T, $($phantom),+>
|
||||
where T: ::serde::Serialize
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: ::serde::Serializer
|
||||
{
|
||||
($(&self.$field,)+).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, $($phantom),+> ::std::cmp::Eq for $name<T, $($phantom),+>
|
||||
where T: ::std::cmp::Eq {}
|
||||
|
||||
impl<T, $($phantom),+> ::std::cmp::PartialEq for $name<T, $($phantom),+>
|
||||
where T: ::std::cmp::PartialEq
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
true $(&& self.$field == other.$field)+
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, $($phantom),+> ::std::hash::Hash for $name<T, $($phantom),+>
|
||||
where T: ::std::hash::Hash
|
||||
{
|
||||
fn hash<H: ::std::hash::Hasher>(&self, h: &mut H) {
|
||||
$(self.$field.hash(h);)+
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
// Copyright 2014 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//! A one-dimensional length, tagged with its units.
|
||||
|
||||
use num_traits;
|
||||
|
||||
|
||||
pub trait Zero {
|
||||
fn zero() -> Self;
|
||||
}
|
||||
|
||||
impl<T: num_traits::Zero> Zero for T {
|
||||
fn zero() -> T { num_traits::Zero::zero() }
|
||||
}
|
||||
|
||||
pub trait One {
|
||||
fn one() -> Self;
|
||||
}
|
||||
|
||||
impl<T: num_traits::One> One for T {
|
||||
fn one() -> T { num_traits::One::one() }
|
||||
}
|
||||
|
||||
|
||||
pub trait Round : Copy { fn round(self) -> Self; }
|
||||
pub trait Floor : Copy { fn floor(self) -> Self; }
|
||||
pub trait Ceil : Copy { fn ceil(self) -> Self; }
|
||||
|
||||
macro_rules! num_int {
|
||||
($ty:ty) => (
|
||||
impl Round for $ty {
|
||||
#[inline]
|
||||
fn round(self) -> $ty { self }
|
||||
}
|
||||
impl Floor for $ty {
|
||||
#[inline]
|
||||
fn floor(self) -> $ty { self }
|
||||
}
|
||||
impl Ceil for $ty {
|
||||
#[inline]
|
||||
fn ceil(self) -> $ty { self }
|
||||
}
|
||||
)
|
||||
}
|
||||
macro_rules! num_float {
|
||||
($ty:ty) => (
|
||||
impl Round for $ty {
|
||||
#[inline]
|
||||
fn round(self) -> $ty { self.round() }
|
||||
}
|
||||
impl Floor for $ty {
|
||||
#[inline]
|
||||
fn floor(self) -> $ty { self.floor() }
|
||||
}
|
||||
impl Ceil for $ty {
|
||||
#[inline]
|
||||
fn ceil(self) -> $ty { self.ceil() }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
num_int!(i16);
|
||||
num_int!(u16);
|
||||
num_int!(i32);
|
||||
num_int!(u32);
|
||||
num_int!(i64);
|
||||
num_int!(u64);
|
||||
num_int!(isize);
|
||||
num_int!(usize);
|
||||
num_float!(f32);
|
||||
num_float!(f64);
|
|
@ -1,995 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::UnknownUnit;
|
||||
use approxeq::ApproxEq;
|
||||
use length::Length;
|
||||
use scale_factor::ScaleFactor;
|
||||
use size::TypedSize2D;
|
||||
use num::*;
|
||||
use num_traits::{Float, NumCast};
|
||||
use std::fmt;
|
||||
use std::ops::{Add, Neg, Mul, Sub, Div};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
define_matrix! {
|
||||
/// A 2d Point tagged with a unit.
|
||||
#[derive(RustcDecodable, RustcEncodable)]
|
||||
pub struct TypedPoint2D<T, U> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// Default 2d point type with no unit.
|
||||
///
|
||||
/// `Point2D` provides the same methods as `TypedPoint2D`.
|
||||
pub type Point2D<T> = TypedPoint2D<T, UnknownUnit>;
|
||||
|
||||
impl<T: Copy + Zero, U> TypedPoint2D<T, U> {
|
||||
/// Constructor, setting all components to zero.
|
||||
#[inline]
|
||||
pub fn zero() -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(Zero::zero(), Zero::zero())
|
||||
}
|
||||
|
||||
/// Convert into a 3d point.
|
||||
#[inline]
|
||||
pub fn to_3d(&self) -> TypedPoint3D<T, U> {
|
||||
TypedPoint3D::new(self.x, self.y, Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedPoint2D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({:?},{:?})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for TypedPoint2D<T, U> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "({},{})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> TypedPoint2D<T, U> {
|
||||
/// Constructor taking scalar values directly.
|
||||
#[inline]
|
||||
pub fn new(x: T, y: T) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D { x: x, y: y, _unit: PhantomData }
|
||||
}
|
||||
|
||||
/// Constructor taking properly typed Lengths instead of scalar values.
|
||||
#[inline]
|
||||
pub fn from_lengths(x: Length<T, U>, y: Length<T, U>) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(x.0, y.0)
|
||||
}
|
||||
|
||||
/// Returns self.x as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn x_typed(&self) -> Length<T, U> { Length::new(self.x) }
|
||||
|
||||
/// Returns self.y as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn y_typed(&self) -> Length<T, U> { Length::new(self.y) }
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Point2D<T> {
|
||||
TypedPoint2D::new(self.x, self.y)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(p: &Point2D<T>) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(p.x, p.y)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 2] {
|
||||
[self.x, self.y]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedPoint2D<T, U>
|
||||
where T: Copy + Mul<T, Output=T> + Add<T, Output=T> + Sub<T, Output=T> {
|
||||
/// Dot product.
|
||||
#[inline]
|
||||
pub fn dot(self, other: TypedPoint2D<T, U>) -> T {
|
||||
self.x * other.x + self.y * other.y
|
||||
}
|
||||
|
||||
/// Returns the norm of the cross product [self.x, self.y, 0] x [other.x, other.y, 0]..
|
||||
#[inline]
|
||||
pub fn cross(self, other: TypedPoint2D<T, U>) -> T {
|
||||
self.x * other.y - self.y * other.x
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn normalize(self) -> Self where T: Float + ApproxEq<T> {
|
||||
let dot = self.dot(self);
|
||||
if dot.approx_eq(&T::zero()) {
|
||||
self
|
||||
} else {
|
||||
self / dot.sqrt()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output=T>, U> Add for TypedPoint2D<T, U> {
|
||||
type Output = TypedPoint2D<T, U>;
|
||||
fn add(self, other: TypedPoint2D<T, U>) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.x + other.x, self.y + other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output=T>, U> Add<TypedSize2D<T, U>> for TypedPoint2D<T, U> {
|
||||
type Output = TypedPoint2D<T, U>;
|
||||
fn add(self, other: TypedSize2D<T, U>) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.x + other.width, self.y + other.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output=T>, U> TypedPoint2D<T, U> {
|
||||
pub fn add_size(&self, other: &TypedSize2D<T, U>) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.x + other.width, self.y + other.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output=T>, U> Sub for TypedPoint2D<T, U> {
|
||||
type Output = TypedPoint2D<T, U>;
|
||||
fn sub(self, other: TypedPoint2D<T, U>) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.x - other.x, self.y - other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: Copy + Neg<Output=T>, U> Neg for TypedPoint2D<T, U> {
|
||||
type Output = TypedPoint2D<T, U>;
|
||||
#[inline]
|
||||
fn neg(self) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(-self.x, -self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float, U> TypedPoint2D<T, U> {
|
||||
pub fn min(self, other: TypedPoint2D<T, U>) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.x.min(other.x), self.y.min(other.y))
|
||||
}
|
||||
|
||||
pub fn max(self, other: TypedPoint2D<T, U>) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.x.max(other.x), self.y.max(other.y))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output=T>, U> Mul<T> for TypedPoint2D<T, U> {
|
||||
type Output = TypedPoint2D<T, U>;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.x * scale, self.y * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U> Div<T> for TypedPoint2D<T, U> {
|
||||
type Output = TypedPoint2D<T, U>;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.x / scale, self.y / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output=T>, U1, U2> Mul<ScaleFactor<T, U1, U2>> for TypedPoint2D<T, U1> {
|
||||
type Output = TypedPoint2D<T, U2>;
|
||||
#[inline]
|
||||
fn mul(self, scale: ScaleFactor<T, U1, U2>) -> TypedPoint2D<T, U2> {
|
||||
TypedPoint2D::new(self.x * scale.get(), self.y * scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U1, U2> Div<ScaleFactor<T, U1, U2>> for TypedPoint2D<T, U2> {
|
||||
type Output = TypedPoint2D<T, U1>;
|
||||
#[inline]
|
||||
fn div(self, scale: ScaleFactor<T, U1, U2>) -> TypedPoint2D<T, U1> {
|
||||
TypedPoint2D::new(self.x / scale.get(), self.y / scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Round, U> TypedPoint2D<T, U> {
|
||||
/// Rounds each component to the nearest integer value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
/// For example `{ -0.1, -0.8 }.round() == { 0.0, -1.0 }`.
|
||||
pub fn round(&self) -> Self {
|
||||
TypedPoint2D::new(self.x.round(), self.y.round())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ceil, U> TypedPoint2D<T, U> {
|
||||
/// Rounds each component to the smallest integer equal or greater than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
/// For example `{ -0.1, -0.8 }.ceil() == { 0.0, 0.0 }`.
|
||||
pub fn ceil(&self) -> Self {
|
||||
TypedPoint2D::new(self.x.ceil(), self.y.ceil())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Floor, U> TypedPoint2D<T, U> {
|
||||
/// Rounds each component to the biggest integer equal or lower than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
/// For example `{ -0.1, -0.8 }.floor() == { -1.0, -1.0 }`.
|
||||
pub fn floor(&self) -> Self {
|
||||
TypedPoint2D::new(self.x.floor(), self.y.floor())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Copy, U> TypedPoint2D<T, U> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
|
||||
pub fn cast<NewT: NumCast + Copy>(&self) -> Option<TypedPoint2D<NewT, U>> {
|
||||
match (NumCast::from(self.x), NumCast::from(self.y)) {
|
||||
(Some(x), Some(y)) => Some(TypedPoint2D::new(x, y)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
|
||||
/// Cast into an `f32` point.
|
||||
pub fn to_f32(&self) -> TypedPoint2D<f32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `usize` point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_usize(&self) -> TypedPoint2D<usize, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an i32 point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_i32(&self) -> TypedPoint2D<i32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an i64 point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_i64(&self) -> TypedPoint2D<i64, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy+ApproxEq<T>, U> ApproxEq<TypedPoint2D<T, U>> for TypedPoint2D<T, U> {
|
||||
#[inline]
|
||||
fn approx_epsilon() -> Self {
|
||||
TypedPoint2D::new(T::approx_epsilon(), T::approx_epsilon())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn approx_eq(&self, other: &Self) -> bool {
|
||||
self.x.approx_eq(&other.x) && self.y.approx_eq(&other.y)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn approx_eq_eps(&self, other: &Self, eps: &Self) -> bool {
|
||||
self.x.approx_eq_eps(&other.x, &eps.x) && self.y.approx_eq_eps(&other.y, &eps.y)
|
||||
}
|
||||
}
|
||||
|
||||
define_matrix! {
|
||||
/// A 3d Point tagged with a unit.
|
||||
#[derive(RustcDecodable, RustcEncodable)]
|
||||
pub struct TypedPoint3D<T, U> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
pub z: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// Default 3d point type with no unit.
|
||||
///
|
||||
/// `Point3D` provides the same methods as `TypedPoint3D`.
|
||||
pub type Point3D<T> = TypedPoint3D<T, UnknownUnit>;
|
||||
|
||||
impl<T: Copy + Zero, U> TypedPoint3D<T, U> {
|
||||
/// Constructor, setting all copmonents to zero.
|
||||
#[inline]
|
||||
pub fn zero() -> TypedPoint3D<T, U> {
|
||||
TypedPoint3D::new(Zero::zero(), Zero::zero(), Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedPoint3D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({:?},{:?},{:?})", self.x, self.y, self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for TypedPoint3D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({},{},{})", self.x, self.y, self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> TypedPoint3D<T, U> {
|
||||
/// Constructor taking scalar values directly.
|
||||
#[inline]
|
||||
pub fn new(x: T, y: T, z: T) -> TypedPoint3D<T, U> {
|
||||
TypedPoint3D { x: x, y: y, z: z, _unit: PhantomData }
|
||||
}
|
||||
|
||||
/// Constructor taking properly typed Lengths instead of scalar values.
|
||||
#[inline]
|
||||
pub fn from_lengths(x: Length<T, U>, y: Length<T, U>, z: Length<T, U>) -> TypedPoint3D<T, U> {
|
||||
TypedPoint3D::new(x.0, y.0, z.0)
|
||||
}
|
||||
|
||||
/// Returns self.x as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn x_typed(&self) -> Length<T, U> { Length::new(self.x) }
|
||||
|
||||
/// Returns self.y as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn y_typed(&self) -> Length<T, U> { Length::new(self.y) }
|
||||
|
||||
/// Returns self.z as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn z_typed(&self) -> Length<T, U> { Length::new(self.z) }
|
||||
|
||||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 3] { [self.x, self.y, self.z] }
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Point3D<T> {
|
||||
TypedPoint3D::new(self.x, self.y, self.z)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(p: &Point3D<T>) -> TypedPoint3D<T, U> {
|
||||
TypedPoint3D::new(p.x, p.y, p.z)
|
||||
}
|
||||
|
||||
/// Convert into a 2d point.
|
||||
#[inline]
|
||||
pub fn to_2d(&self) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Mul<T, Output=T> +
|
||||
Add<T, Output=T> +
|
||||
Sub<T, Output=T> +
|
||||
Copy, U> TypedPoint3D<T, U> {
|
||||
|
||||
// Dot product.
|
||||
#[inline]
|
||||
pub fn dot(self, other: TypedPoint3D<T, U>) -> T {
|
||||
self.x * other.x +
|
||||
self.y * other.y +
|
||||
self.z * other.z
|
||||
}
|
||||
|
||||
// Cross product.
|
||||
#[inline]
|
||||
pub fn cross(self, other: TypedPoint3D<T, U>) -> TypedPoint3D<T, U> {
|
||||
TypedPoint3D::new(self.y * other.z - self.z * other.y,
|
||||
self.z * other.x - self.x * other.z,
|
||||
self.x * other.y - self.y * other.x)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn normalize(self) -> Self where T: Float + ApproxEq<T> {
|
||||
let dot = self.dot(self);
|
||||
if dot.approx_eq(&T::zero()) {
|
||||
self
|
||||
} else {
|
||||
self / dot.sqrt()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output=T>, U> Add for TypedPoint3D<T, U> {
|
||||
type Output = TypedPoint3D<T, U>;
|
||||
fn add(self, other: TypedPoint3D<T, U>) -> TypedPoint3D<T, U> {
|
||||
TypedPoint3D::new(self.x + other.x,
|
||||
self.y + other.y,
|
||||
self.z + other.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output=T>, U> Sub for TypedPoint3D<T, U> {
|
||||
type Output = TypedPoint3D<T, U>;
|
||||
fn sub(self, other: TypedPoint3D<T, U>) -> TypedPoint3D<T, U> {
|
||||
TypedPoint3D::new(self.x - other.x,
|
||||
self.y - other.y,
|
||||
self.z - other.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: Copy + Neg<Output=T>, U> Neg for TypedPoint3D<T, U> {
|
||||
type Output = TypedPoint3D<T, U>;
|
||||
#[inline]
|
||||
fn neg(self) -> TypedPoint3D<T, U> {
|
||||
TypedPoint3D::new(-self.x, -self.y, -self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output=T>, U> Mul<T> for TypedPoint3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self {
|
||||
Self::new(self.x * scale, self.y * scale, self.z * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U> Div<T> for TypedPoint3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self {
|
||||
Self::new(self.x / scale, self.y / scale, self.z / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float, U> TypedPoint3D<T, U> {
|
||||
pub fn min(self, other: TypedPoint3D<T, U>) -> TypedPoint3D<T, U> {
|
||||
TypedPoint3D::new(self.x.min(other.x),
|
||||
self.y.min(other.y),
|
||||
self.z.min(other.z))
|
||||
}
|
||||
|
||||
pub fn max(self, other: TypedPoint3D<T, U>) -> TypedPoint3D<T, U> {
|
||||
TypedPoint3D::new(self.x.max(other.x), self.y.max(other.y),
|
||||
self.z.max(other.z))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Round, U> TypedPoint3D<T, U> {
|
||||
/// Rounds each component to the nearest integer value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
pub fn round(&self) -> Self {
|
||||
TypedPoint3D::new(self.x.round(), self.y.round(), self.z.round())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ceil, U> TypedPoint3D<T, U> {
|
||||
/// Rounds each component to the smallest integer equal or greater than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
pub fn ceil(&self) -> Self {
|
||||
TypedPoint3D::new(self.x.ceil(), self.y.ceil(), self.z.ceil())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Floor, U> TypedPoint3D<T, U> {
|
||||
/// Rounds each component to the biggest integer equal or lower than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
pub fn floor(&self) -> Self {
|
||||
TypedPoint3D::new(self.x.floor(), self.y.floor(), self.z.floor())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Copy, U> TypedPoint3D<T, U> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using round(), ceil or floor() before casting.
|
||||
pub fn cast<NewT: NumCast + Copy>(&self) -> Option<TypedPoint3D<NewT, U>> {
|
||||
match (NumCast::from(self.x),
|
||||
NumCast::from(self.y),
|
||||
NumCast::from(self.z)) {
|
||||
(Some(x), Some(y), Some(z)) => Some(TypedPoint3D::new(x, y, z)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
|
||||
/// Cast into an `f32` point.
|
||||
pub fn to_f32(&self) -> TypedPoint3D<f32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `usize` point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_usize(&self) -> TypedPoint3D<usize, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `i32` point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_i32(&self) -> TypedPoint3D<i32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `i64` point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_i64(&self) -> TypedPoint3D<i64, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy+ApproxEq<T>, U> ApproxEq<TypedPoint3D<T, U>> for TypedPoint3D<T, U> {
|
||||
#[inline]
|
||||
fn approx_epsilon() -> Self {
|
||||
TypedPoint3D::new(T::approx_epsilon(), T::approx_epsilon(), T::approx_epsilon())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn approx_eq(&self, other: &Self) -> bool {
|
||||
self.x.approx_eq(&other.x)
|
||||
&& self.y.approx_eq(&other.y)
|
||||
&& self.z.approx_eq(&other.z)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn approx_eq_eps(&self, other: &Self, eps: &Self) -> bool {
|
||||
self.x.approx_eq_eps(&other.x, &eps.x)
|
||||
&& self.y.approx_eq_eps(&other.y, &eps.y)
|
||||
&& self.z.approx_eq_eps(&other.z, &eps.z)
|
||||
}
|
||||
}
|
||||
|
||||
define_matrix! {
|
||||
/// A 4d Point tagged with a unit.
|
||||
#[derive(RustcDecodable, RustcEncodable)]
|
||||
pub struct TypedPoint4D<T, U> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
pub z: T,
|
||||
pub w: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// Default 4d point with no unit.
|
||||
///
|
||||
/// `Point4D` provides the same methods as `TypedPoint4D`.
|
||||
pub type Point4D<T> = TypedPoint4D<T, UnknownUnit>;
|
||||
|
||||
impl<T: Copy + Zero, U> TypedPoint4D<T, U> {
|
||||
/// Constructor, setting all copmonents to zero.
|
||||
#[inline]
|
||||
pub fn zero() -> TypedPoint4D<T, U> {
|
||||
TypedPoint4D::new(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedPoint4D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({:?},{:?},{:?},{:?})", self.x, self.y, self.z, self.w)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for TypedPoint4D<T, U> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "({},{},{},{})", self.x, self.y, self.z, self.w)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> TypedPoint4D<T, U> {
|
||||
/// Constructor taking scalar values directly.
|
||||
#[inline]
|
||||
pub fn new(x: T, y: T, z: T, w: T) -> TypedPoint4D<T, U> {
|
||||
TypedPoint4D { x: x, y: y, z: z, w: w, _unit: PhantomData }
|
||||
}
|
||||
|
||||
/// Constructor taking properly typed Lengths instead of scalar values.
|
||||
#[inline]
|
||||
pub fn from_lengths(x: Length<T, U>,
|
||||
y: Length<T, U>,
|
||||
z: Length<T, U>,
|
||||
w: Length<T, U>) -> TypedPoint4D<T, U> {
|
||||
TypedPoint4D::new(x.0, y.0, z.0, w.0)
|
||||
}
|
||||
|
||||
/// Returns self.x as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn x_typed(&self) -> Length<T, U> { Length::new(self.x) }
|
||||
|
||||
/// Returns self.y as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn y_typed(&self) -> Length<T, U> { Length::new(self.y) }
|
||||
|
||||
/// Returns self.z as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn z_typed(&self) -> Length<T, U> { Length::new(self.z) }
|
||||
|
||||
/// Returns self.w as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn w_typed(&self) -> Length<T, U> { Length::new(self.w) }
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Point4D<T> {
|
||||
TypedPoint4D::new(self.x, self.y, self.z, self.w)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(p: &Point4D<T>) -> TypedPoint4D<T, U> {
|
||||
TypedPoint4D::new(p.x, p.y, p.z, p.w)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 4] {
|
||||
[self.x, self.y, self.z, self.w]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U> TypedPoint4D<T, U> {
|
||||
/// Convert into a 2d point.
|
||||
#[inline]
|
||||
pub fn to_2d(self) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.x / self.w, self.y / self.w)
|
||||
}
|
||||
|
||||
/// Convert into a 3d point.
|
||||
#[inline]
|
||||
pub fn to_3d(self) -> TypedPoint3D<T, U> {
|
||||
TypedPoint3D::new(self.x / self.w, self.y / self.w, self.z / self.w)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output=T>, U> Add for TypedPoint4D<T, U> {
|
||||
type Output = TypedPoint4D<T, U>;
|
||||
fn add(self, other: TypedPoint4D<T, U>) -> TypedPoint4D<T, U> {
|
||||
TypedPoint4D::new(self.x + other.x,
|
||||
self.y + other.y,
|
||||
self.z + other.z,
|
||||
self.w + other.w)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output=T>, U> Sub for TypedPoint4D<T, U> {
|
||||
type Output = TypedPoint4D<T, U>;
|
||||
fn sub(self, other: TypedPoint4D<T, U>) -> TypedPoint4D<T, U> {
|
||||
TypedPoint4D::new(self.x - other.x,
|
||||
self.y - other.y,
|
||||
self.z - other.z,
|
||||
self.w - other.w)
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: Copy + Neg<Output=T>, U> Neg for TypedPoint4D<T, U> {
|
||||
type Output = TypedPoint4D<T, U>;
|
||||
#[inline]
|
||||
fn neg(self) -> TypedPoint4D<T, U> {
|
||||
TypedPoint4D::new(-self.x, -self.y, -self.z, -self.w)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float, U> TypedPoint4D<T, U> {
|
||||
pub fn min(self, other: TypedPoint4D<T, U>) -> TypedPoint4D<T, U> {
|
||||
TypedPoint4D::new(self.x.min(other.x), self.y.min(other.y),
|
||||
self.z.min(other.z), self.w.min(other.w))
|
||||
}
|
||||
|
||||
pub fn max(self, other: TypedPoint4D<T, U>) -> TypedPoint4D<T, U> {
|
||||
TypedPoint4D::new(self.x.max(other.x), self.y.max(other.y),
|
||||
self.z.max(other.z), self.w.max(other.w))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Round, U> TypedPoint4D<T, U> {
|
||||
/// Rounds each component to the nearest integer value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
pub fn round(&self) -> Self {
|
||||
TypedPoint4D::new(self.x.round(), self.y.round(), self.z.round(), self.w.round())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ceil, U> TypedPoint4D<T, U> {
|
||||
/// Rounds each component to the smallest integer equal or greater than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
pub fn ceil(&self) -> Self {
|
||||
TypedPoint4D::new(self.x.ceil(), self.y.ceil(), self.z.ceil(), self.w.ceil())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Floor, U> TypedPoint4D<T, U> {
|
||||
/// Rounds each component to the biggest integer equal or lower than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
pub fn floor(&self) -> Self {
|
||||
TypedPoint4D::new(self.x.floor(), self.y.floor(), self.z.floor(), self.w.floor())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Copy, U> TypedPoint4D<T, U> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
|
||||
pub fn cast<NewT: NumCast + Copy>(&self) -> Option<TypedPoint4D<NewT, U>> {
|
||||
match (NumCast::from(self.x),
|
||||
NumCast::from(self.y),
|
||||
NumCast::from(self.z),
|
||||
NumCast::from(self.w)) {
|
||||
(Some(x), Some(y), Some(z), Some(w)) => Some(TypedPoint4D::new(x, y, z, w)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
|
||||
/// Cast into an `f32` point.
|
||||
pub fn to_f32(&self) -> TypedPoint4D<f32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `usize` point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_usize(&self) -> TypedPoint4D<usize, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `i32` point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_i32(&self) -> TypedPoint4D<i32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `i64` point, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point points, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_i64(&self) -> TypedPoint4D<i64, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ApproxEq<T>, U> ApproxEq<T> for TypedPoint4D<T, U> {
|
||||
fn approx_epsilon() -> T {
|
||||
T::approx_epsilon()
|
||||
}
|
||||
|
||||
fn approx_eq_eps(&self, other: &Self, approx_epsilon: &T) -> bool {
|
||||
self.x.approx_eq_eps(&other.x, approx_epsilon)
|
||||
&& self.y.approx_eq_eps(&other.y, approx_epsilon)
|
||||
&& self.z.approx_eq_eps(&other.z, approx_epsilon)
|
||||
&& self.w.approx_eq_eps(&other.w, approx_epsilon)
|
||||
}
|
||||
|
||||
fn approx_eq(&self, other: &Self) -> bool {
|
||||
self.approx_eq_eps(&other, &Self::approx_epsilon())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn point2<T: Copy, U>(x: T, y: T) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(x, y)
|
||||
}
|
||||
|
||||
pub fn point3<T: Copy, U>(x: T, y: T, z: T) -> TypedPoint3D<T, U> {
|
||||
TypedPoint3D::new(x, y, z)
|
||||
}
|
||||
|
||||
pub fn point4<T: Copy, U>(x: T, y: T, z: T, w: T) -> TypedPoint4D<T, U> {
|
||||
TypedPoint4D::new(x, y, z, w)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod point2d {
|
||||
use super::Point2D;
|
||||
|
||||
#[test]
|
||||
pub fn test_scalar_mul() {
|
||||
let p1: Point2D<f32> = Point2D::new(3.0, 5.0);
|
||||
|
||||
let result = p1 * 5.0;
|
||||
|
||||
assert_eq!(result, Point2D::new(15.0, 25.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_dot() {
|
||||
let p1: Point2D<f32> = Point2D::new(2.0, 7.0);
|
||||
let p2: Point2D<f32> = Point2D::new(13.0, 11.0);
|
||||
assert_eq!(p1.dot(p2), 103.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_cross() {
|
||||
let p1: Point2D<f32> = Point2D::new(4.0, 7.0);
|
||||
let p2: Point2D<f32> = Point2D::new(13.0, 8.0);
|
||||
let r = p1.cross(p2);
|
||||
assert_eq!(r, -59.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_normalize() {
|
||||
let p0: Point2D<f32> = Point2D::zero();
|
||||
let p1: Point2D<f32> = Point2D::new(4.0, 0.0);
|
||||
let p2: Point2D<f32> = Point2D::new(3.0, -4.0);
|
||||
assert_eq!(p0.normalize(), p0);
|
||||
assert_eq!(p1.normalize(), Point2D::new(1.0, 0.0));
|
||||
assert_eq!(p2.normalize(), Point2D::new(0.6, -0.8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_min() {
|
||||
let p1 = Point2D::new(1.0, 3.0);
|
||||
let p2 = Point2D::new(2.0, 2.0);
|
||||
|
||||
let result = p1.min(p2);
|
||||
|
||||
assert_eq!(result, Point2D::new(1.0, 2.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_max() {
|
||||
let p1 = Point2D::new(1.0, 3.0);
|
||||
let p2 = Point2D::new(2.0, 2.0);
|
||||
|
||||
let result = p1.max(p2);
|
||||
|
||||
assert_eq!(result, Point2D::new(2.0, 3.0));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod typedpoint2d {
|
||||
use super::TypedPoint2D;
|
||||
use scale_factor::ScaleFactor;
|
||||
|
||||
pub enum Mm {}
|
||||
pub enum Cm {}
|
||||
|
||||
pub type Point2DMm<T> = TypedPoint2D<T, Mm>;
|
||||
pub type Point2DCm<T> = TypedPoint2D<T, Cm>;
|
||||
|
||||
#[test]
|
||||
pub fn test_add() {
|
||||
let p1 = Point2DMm::new(1.0, 2.0);
|
||||
let p2 = Point2DMm::new(3.0, 4.0);
|
||||
|
||||
let result = p1 + p2;
|
||||
|
||||
assert_eq!(result, Point2DMm::new(4.0, 6.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_scalar_mul() {
|
||||
let p1 = Point2DMm::new(1.0, 2.0);
|
||||
let cm_per_mm: ScaleFactor<f32, Mm, Cm> = ScaleFactor::new(0.1);
|
||||
|
||||
let result = p1 * cm_per_mm;
|
||||
|
||||
assert_eq!(result, Point2DCm::new(0.1, 0.2));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod point3d {
|
||||
use super::Point3D;
|
||||
|
||||
#[test]
|
||||
pub fn test_dot() {
|
||||
let p1 = Point3D::new(7.0, 21.0, 32.0);
|
||||
let p2 = Point3D::new(43.0, 5.0, 16.0);
|
||||
assert_eq!(p1.dot(p2), 918.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_cross() {
|
||||
let p1 = Point3D::new(4.0, 7.0, 9.0);
|
||||
let p2 = Point3D::new(13.0, 8.0, 3.0);
|
||||
let p3 = p1.cross(p2);
|
||||
assert_eq!(p3, Point3D::new(-51.0, 105.0, -59.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_normalize() {
|
||||
let p0: Point3D<f32> = Point3D::zero();
|
||||
let p1: Point3D<f32> = Point3D::new(0.0, -6.0, 0.0);
|
||||
let p2: Point3D<f32> = Point3D::new(1.0, 2.0, -2.0);
|
||||
assert_eq!(p0.normalize(), p0);
|
||||
assert_eq!(p1.normalize(), Point3D::new(0.0, -1.0, 0.0));
|
||||
assert_eq!(p2.normalize(), Point3D::new(1.0/3.0, 2.0/3.0, -2.0/3.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_min() {
|
||||
let p1 = Point3D::new(1.0, 3.0, 5.0);
|
||||
let p2 = Point3D::new(2.0, 2.0, -1.0);
|
||||
|
||||
let result = p1.min(p2);
|
||||
|
||||
assert_eq!(result, Point3D::new(1.0, 2.0, -1.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_max() {
|
||||
let p1 = Point3D::new(1.0, 3.0, 5.0);
|
||||
let p2 = Point3D::new(2.0, 2.0, -1.0);
|
||||
|
||||
let result = p1.max(p2);
|
||||
|
||||
assert_eq!(result, Point3D::new(2.0, 3.0, 5.0));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod point4d {
|
||||
use super::Point4D;
|
||||
|
||||
#[test]
|
||||
pub fn test_add() {
|
||||
let p1 = Point4D::new(7.0, 21.0, 32.0, 1.0);
|
||||
let p2 = Point4D::new(43.0, 5.0, 16.0, 2.0);
|
||||
|
||||
let result = p1 + p2;
|
||||
|
||||
assert_eq!(result, Point4D::new(50.0, 26.0, 48.0, 3.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_sub() {
|
||||
let p1 = Point4D::new(7.0, 21.0, 32.0, 1.0);
|
||||
let p2 = Point4D::new(43.0, 5.0, 16.0, 2.0);
|
||||
|
||||
let result = p1 - p2;
|
||||
|
||||
assert_eq!(result, Point4D::new(-36.0, 16.0, 16.0, -1.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_min() {
|
||||
let p1 = Point4D::new(1.0, 3.0, 5.0, 7.0);
|
||||
let p2 = Point4D::new(2.0, 2.0, -1.0, 10.0);
|
||||
|
||||
let result = p1.min(p2);
|
||||
|
||||
assert_eq!(result, Point4D::new(1.0, 2.0, -1.0, 7.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_max() {
|
||||
let p1 = Point4D::new(1.0, 3.0, 5.0, 7.0);
|
||||
let p2 = Point4D::new(2.0, 2.0, -1.0, 10.0);
|
||||
|
||||
let result = p1.max(p2);
|
||||
|
||||
assert_eq!(result, Point4D::new(2.0, 3.0, 5.0, 10.0));
|
||||
}
|
||||
}
|
|
@ -1,671 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::UnknownUnit;
|
||||
use length::Length;
|
||||
use scale_factor::ScaleFactor;
|
||||
use num::*;
|
||||
use point::TypedPoint2D;
|
||||
use size::TypedSize2D;
|
||||
|
||||
use heapsize::HeapSizeOf;
|
||||
use num_traits::NumCast;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::cmp::PartialOrd;
|
||||
use std::fmt;
|
||||
use std::ops::{Add, Sub, Mul, Div};
|
||||
|
||||
/// A 2d Rectangle optionally tagged with a unit.
|
||||
#[derive(RustcDecodable, RustcEncodable)]
|
||||
pub struct TypedRect<T, U = UnknownUnit> {
|
||||
pub origin: TypedPoint2D<T, U>,
|
||||
pub size: TypedSize2D<T, U>,
|
||||
}
|
||||
|
||||
/// The default rectangle type with no unit.
|
||||
pub type Rect<T> = TypedRect<T, UnknownUnit>;
|
||||
|
||||
impl<T: HeapSizeOf, U> HeapSizeOf for TypedRect<T, U> {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.origin.heap_size_of_children() + self.size.heap_size_of_children()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Deserialize, U> Deserialize for TypedRect<T, U> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where D: Deserializer
|
||||
{
|
||||
let (origin, size) = try!(Deserialize::deserialize(deserializer));
|
||||
Ok(TypedRect::new(origin, size))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Serialize, U> Serialize for TypedRect<T, U> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer
|
||||
{
|
||||
(&self.origin, &self.size).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> Copy for TypedRect<T, U> {}
|
||||
|
||||
impl<T: Copy, U> Clone for TypedRect<T, U> {
|
||||
fn clone(&self) -> TypedRect<T, U> { *self }
|
||||
}
|
||||
|
||||
impl<T: PartialEq, U> PartialEq<TypedRect<T, U>> for TypedRect<T, U> {
|
||||
fn eq(&self, other: &TypedRect<T, U>) -> bool {
|
||||
self.origin.eq(&other.origin) && self.size.eq(&other.size)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq, U> Eq for TypedRect<T, U> {}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedRect<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "TypedRect({:?} at {:?})", self.size, self.origin)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for TypedRect<T, U> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "Rect({} at {})", self.size, self.origin)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedRect<T, U> {
|
||||
/// Constructor.
|
||||
pub fn new(origin: TypedPoint2D<T, U>, size: TypedSize2D<T, U>) -> TypedRect<T, U> {
|
||||
TypedRect {
|
||||
origin: origin,
|
||||
size: size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedRect<T, U>
|
||||
where T: Copy + Clone + Zero + PartialOrd + PartialEq + Add<T, Output=T> + Sub<T, Output=T> {
|
||||
#[inline]
|
||||
pub fn intersects(&self, other: &TypedRect<T, U>) -> bool {
|
||||
self.origin.x < other.origin.x + other.size.width &&
|
||||
other.origin.x < self.origin.x + self.size.width &&
|
||||
self.origin.y < other.origin.y + other.size.height &&
|
||||
other.origin.y < self.origin.y + self.size.height
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max_x(&self) -> T {
|
||||
self.origin.x + self.size.width
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn min_x(&self) -> T {
|
||||
self.origin.x
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max_y(&self) -> T {
|
||||
self.origin.y + self.size.height
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn min_y(&self) -> T {
|
||||
self.origin.y
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max_x_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.max_x())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn min_x_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.min_x())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max_y_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.max_y())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn min_y_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.min_y())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn intersection(&self, other: &TypedRect<T, U>) -> Option<TypedRect<T, U>> {
|
||||
if !self.intersects(other) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let upper_left = TypedPoint2D::new(max(self.min_x(), other.min_x()),
|
||||
max(self.min_y(), other.min_y()));
|
||||
let lower_right_x = min(self.max_x(), other.max_x());
|
||||
let lower_right_y = min(self.max_y(), other.max_y());
|
||||
|
||||
Some(TypedRect::new(upper_left, TypedSize2D::new(lower_right_x - upper_left.x,
|
||||
lower_right_y - upper_left.y)))
|
||||
}
|
||||
|
||||
/// Translates the rect by a vector.
|
||||
#[inline]
|
||||
pub fn translate(&self, other: &TypedPoint2D<T, U>) -> TypedRect<T, U> {
|
||||
TypedRect::new(
|
||||
TypedPoint2D::new(self.origin.x + other.x, self.origin.y + other.y),
|
||||
self.size
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns true if this rectangle contains the point. Points are considered
|
||||
/// in the rectangle if they are on the left or top edge, but outside if they
|
||||
/// are on the right or bottom edge.
|
||||
#[inline]
|
||||
pub fn contains(&self, other: &TypedPoint2D<T, U>) -> bool {
|
||||
self.origin.x <= other.x && other.x < self.origin.x + self.size.width &&
|
||||
self.origin.y <= other.y && other.y < self.origin.y + self.size.height
|
||||
}
|
||||
|
||||
/// Returns true if this rectangle contains the interior of rect. Always
|
||||
/// returns true if rect is empty, and always returns false if rect is
|
||||
/// nonempty but this rectangle is empty.
|
||||
#[inline]
|
||||
pub fn contains_rect(&self, rect: &TypedRect<T, U>) -> bool {
|
||||
rect.is_empty() ||
|
||||
(self.min_x() <= rect.min_x() && rect.max_x() <= self.max_x() &&
|
||||
self.min_y() <= rect.min_y() && rect.max_y() <= self.max_y())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inflate(&self, width: T, height: T) -> TypedRect<T, U> {
|
||||
TypedRect::new(
|
||||
TypedPoint2D::new(self.origin.x - width, self.origin.y - height),
|
||||
TypedSize2D::new(self.size.width + width + width, self.size.height + height + height),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inflate_typed(&self, width: Length<T, U>, height: Length<T, U>) -> TypedRect<T, U> {
|
||||
self.inflate(width.get(), height.get())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn top_right(&self) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.max_x(), self.origin.y)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bottom_left(&self) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.origin.x, self.max_y())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bottom_right(&self) -> TypedPoint2D<T, U> {
|
||||
TypedPoint2D::new(self.max_x(), self.max_y())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn translate_by_size(&self, size: &TypedSize2D<T, U>) -> TypedRect<T, U> {
|
||||
self.translate(&TypedPoint2D::new(size.width, size.height))
|
||||
}
|
||||
|
||||
/// Returns the smallest rectangle containing the four points.
|
||||
pub fn from_points(points: &[TypedPoint2D<T, U>]) -> Self {
|
||||
if points.len() == 0 {
|
||||
return TypedRect::zero();
|
||||
}
|
||||
let (mut min_x, mut min_y) = (points[0].x, points[0].y);
|
||||
let (mut max_x, mut max_y) = (min_x, min_y);
|
||||
for point in &points[1..] {
|
||||
if point.x < min_x {
|
||||
min_x = point.x
|
||||
}
|
||||
if point.x > max_x {
|
||||
max_x = point.x
|
||||
}
|
||||
if point.y < min_y {
|
||||
min_y = point.y
|
||||
}
|
||||
if point.y > max_y {
|
||||
max_y = point.y
|
||||
}
|
||||
}
|
||||
TypedRect::new(TypedPoint2D::new(min_x, min_y),
|
||||
TypedSize2D::new(max_x - min_x, max_y - min_y))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedRect<T, U>
|
||||
where T: Copy + Clone + PartialOrd + Add<T, Output=T> + Sub<T, Output=T> + Zero {
|
||||
#[inline]
|
||||
pub fn union(&self, other: &TypedRect<T, U>) -> TypedRect<T, U> {
|
||||
if self.size == Zero::zero() {
|
||||
return *other;
|
||||
}
|
||||
if other.size == Zero::zero() {
|
||||
return *self;
|
||||
}
|
||||
|
||||
let upper_left = TypedPoint2D::new(min(self.min_x(), other.min_x()),
|
||||
min(self.min_y(), other.min_y()));
|
||||
|
||||
let lower_right_x = max(self.max_x(), other.max_x());
|
||||
let lower_right_y = max(self.max_y(), other.max_y());
|
||||
|
||||
TypedRect::new(
|
||||
upper_left,
|
||||
TypedSize2D::new(lower_right_x - upper_left.x, lower_right_y - upper_left.y)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedRect<T, U> {
|
||||
#[inline]
|
||||
pub fn scale<Scale: Copy>(&self, x: Scale, y: Scale) -> TypedRect<T, U>
|
||||
where T: Copy + Clone + Mul<Scale, Output=T> {
|
||||
TypedRect::new(
|
||||
TypedPoint2D::new(self.origin.x * x, self.origin.y * y),
|
||||
TypedSize2D::new(self.size.width * x, self.size.height * y)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + PartialEq + Zero, U> TypedRect<T, U> {
|
||||
/// Constructor, setting all sides to zero.
|
||||
pub fn zero() -> TypedRect<T, U> {
|
||||
TypedRect::new(
|
||||
TypedPoint2D::zero(),
|
||||
TypedSize2D::zero(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns true if the size is zero, regardless of the origin's value.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.size.width == Zero::zero() || self.size.height == Zero::zero()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn min<T: Clone + PartialOrd>(x: T, y: T) -> T {
|
||||
if x <= y { x } else { y }
|
||||
}
|
||||
|
||||
pub fn max<T: Clone + PartialOrd>(x: T, y: T) -> T {
|
||||
if x >= y { x } else { y }
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output=T>, U> Mul<T> for TypedRect<T, U> {
|
||||
type Output = TypedRect<T, U>;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> TypedRect<T, U> {
|
||||
TypedRect::new(self.origin * scale, self.size * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U> Div<T> for TypedRect<T, U> {
|
||||
type Output = TypedRect<T, U>;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> TypedRect<T, U> {
|
||||
TypedRect::new(self.origin / scale, self.size / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output=T>, U1, U2> Mul<ScaleFactor<T, U1, U2>> for TypedRect<T, U1> {
|
||||
type Output = TypedRect<T, U2>;
|
||||
#[inline]
|
||||
fn mul(self, scale: ScaleFactor<T, U1, U2>) -> TypedRect<T, U2> {
|
||||
TypedRect::new(self.origin * scale, self.size * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U1, U2> Div<ScaleFactor<T, U1, U2>> for TypedRect<T, U2> {
|
||||
type Output = TypedRect<T, U1>;
|
||||
#[inline]
|
||||
fn div(self, scale: ScaleFactor<T, U1, U2>) -> TypedRect<T, U1> {
|
||||
TypedRect::new(self.origin / scale, self.size / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, Unit> TypedRect<T, Unit> {
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
pub fn to_untyped(&self) -> Rect<T> {
|
||||
TypedRect::new(self.origin.to_untyped(), self.size.to_untyped())
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
pub fn from_untyped(r: &Rect<T>) -> TypedRect<T, Unit> {
|
||||
TypedRect::new(TypedPoint2D::from_untyped(&r.origin), TypedSize2D::from_untyped(&r.size))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T0: NumCast + Copy, Unit> TypedRect<T0, Unit> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using round(), round_in or round_out() before casting.
|
||||
pub fn cast<T1: NumCast + Copy>(&self) -> Option<TypedRect<T1, Unit>> {
|
||||
match (self.origin.cast(), self.size.cast()) {
|
||||
(Some(origin), Some(size)) => Some(TypedRect::new(origin, size)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Floor + Ceil + Round + Add<T, Output=T> + Sub<T, Output=T>, U> TypedRect<T, U> {
|
||||
/// Return a rectangle with edges rounded to integer coordinates, such that
|
||||
/// the returned rectangle has the same set of pixel centers as the original
|
||||
/// one.
|
||||
/// Edges at offset 0.5 round up.
|
||||
/// Suitable for most places where integral device coordinates
|
||||
/// are needed, but note that any translation should be applied first to
|
||||
/// avoid pixel rounding errors.
|
||||
/// Note that this is *not* rounding to nearest integer if the values are negative.
|
||||
/// They are always rounding as floor(n + 0.5).
|
||||
pub fn round(&self) -> Self {
|
||||
let origin = self.origin.round();
|
||||
let size = self.origin.add_size(&self.size).round() - origin;
|
||||
TypedRect::new(origin, TypedSize2D::new(size.x, size.y))
|
||||
}
|
||||
|
||||
/// Return a rectangle with edges rounded to integer coordinates, such that
|
||||
/// the original rectangle contains the resulting rectangle.
|
||||
pub fn round_in(&self) -> Self {
|
||||
let origin = self.origin.ceil();
|
||||
let size = self.origin.add_size(&self.size).floor() - origin;
|
||||
TypedRect::new(origin, TypedSize2D::new(size.x, size.y))
|
||||
}
|
||||
|
||||
/// Return a rectangle with edges rounded to integer coordinates, such that
|
||||
/// the original rectangle is contained in the resulting rectangle.
|
||||
pub fn round_out(&self) -> Self {
|
||||
let origin = self.origin.floor();
|
||||
let size = self.origin.add_size(&self.size).ceil() - origin;
|
||||
TypedRect::new(origin, TypedSize2D::new(size.x, size.y))
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
impl<T: NumCast + Copy, Unit> TypedRect<T, Unit> {
|
||||
/// Cast into an `f32` rectangle.
|
||||
pub fn to_f32(&self) -> TypedRect<f32, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `usize` rectangle, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point rectangles, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
pub fn to_usize(&self) -> TypedRect<usize, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `i32` rectangle, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point rectangles, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
pub fn to_i32(&self) -> TypedRect<i32, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `i64` rectangle, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point rectangles, it is worth considering whether
|
||||
/// to `round()`, `round_in()` or `round_out()` before the cast in order to
|
||||
/// obtain the desired conversion behavior.
|
||||
pub fn to_i64(&self) -> TypedRect<i64, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorthand for `TypedRect::new(TypedPoint2D::new(x, y), TypedSize2D::new(w, h))`.
|
||||
pub fn rect<T: Copy, U>(x: T, y: T, w: T, h: T) -> TypedRect<T, U> {
|
||||
TypedRect::new(TypedPoint2D::new(x, y), TypedSize2D::new(w, h))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use point::Point2D;
|
||||
use size::Size2D;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_min_max() {
|
||||
assert!(min(0u32, 1u32) == 0u32);
|
||||
assert!(min(-1.0f32, 0.0f32) == -1.0f32);
|
||||
|
||||
assert!(max(0u32, 1u32) == 1u32);
|
||||
assert!(max(-1.0f32, 0.0f32) == 0.0f32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_translate() {
|
||||
let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
|
||||
let pp = p.translate(&Point2D::new(10,15));
|
||||
|
||||
assert!(pp.size.width == 50);
|
||||
assert!(pp.size.height == 40);
|
||||
assert!(pp.origin.x == 10);
|
||||
assert!(pp.origin.y == 15);
|
||||
|
||||
|
||||
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
|
||||
let rr = r.translate(&Point2D::new(0,-10));
|
||||
|
||||
assert!(rr.size.width == 50);
|
||||
assert!(rr.size.height == 40);
|
||||
assert!(rr.origin.x == -10);
|
||||
assert!(rr.origin.y == -15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_translate_by_size() {
|
||||
let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
|
||||
let pp = p.translate_by_size(&Size2D::new(10,15));
|
||||
|
||||
assert!(pp.size.width == 50);
|
||||
assert!(pp.size.height == 40);
|
||||
assert!(pp.origin.x == 10);
|
||||
assert!(pp.origin.y == 15);
|
||||
|
||||
|
||||
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
|
||||
let rr = r.translate_by_size(&Size2D::new(0,-10));
|
||||
|
||||
assert!(rr.size.width == 50);
|
||||
assert!(rr.size.height == 40);
|
||||
assert!(rr.origin.x == -10);
|
||||
assert!(rr.origin.y == -15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_union() {
|
||||
let p = Rect::new(Point2D::new(0, 0), Size2D::new(50, 40));
|
||||
let q = Rect::new(Point2D::new(20,20), Size2D::new(5, 5));
|
||||
let r = Rect::new(Point2D::new(-15, -30), Size2D::new(200, 15));
|
||||
let s = Rect::new(Point2D::new(20, -15), Size2D::new(250, 200));
|
||||
|
||||
let pq = p.union(&q);
|
||||
assert!(pq.origin == Point2D::new(0, 0));
|
||||
assert!(pq.size == Size2D::new(50, 40));
|
||||
|
||||
let pr = p.union(&r);
|
||||
assert!(pr.origin == Point2D::new(-15, -30));
|
||||
assert!(pr.size == Size2D::new(200, 70));
|
||||
|
||||
let ps = p.union(&s);
|
||||
assert!(ps.origin == Point2D::new(0, -15));
|
||||
assert!(ps.size == Size2D::new(270, 200));
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_intersection() {
|
||||
let p = Rect::new(Point2D::new(0, 0), Size2D::new(10, 20));
|
||||
let q = Rect::new(Point2D::new(5, 15), Size2D::new(10, 10));
|
||||
let r = Rect::new(Point2D::new(-5, -5), Size2D::new(8, 8));
|
||||
|
||||
let pq = p.intersection(&q);
|
||||
assert!(pq.is_some());
|
||||
let pq = pq.unwrap();
|
||||
assert!(pq.origin == Point2D::new(5, 15));
|
||||
assert!(pq.size == Size2D::new(5, 5));
|
||||
|
||||
let pr = p.intersection(&r);
|
||||
assert!(pr.is_some());
|
||||
let pr = pr.unwrap();
|
||||
assert!(pr.origin == Point2D::new(0, 0));
|
||||
assert!(pr.size == Size2D::new(3, 3));
|
||||
|
||||
let qr = q.intersection(&r);
|
||||
assert!(qr.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_contains() {
|
||||
let r = Rect::new(Point2D::new(-20, 15), Size2D::new(100, 200));
|
||||
|
||||
assert!(r.contains(&Point2D::new(0, 50)));
|
||||
assert!(r.contains(&Point2D::new(-10, 200)));
|
||||
|
||||
// The `contains` method is inclusive of the top/left edges, but not the
|
||||
// bottom/right edges.
|
||||
assert!(r.contains(&Point2D::new(-20, 15)));
|
||||
assert!(!r.contains(&Point2D::new(80, 15)));
|
||||
assert!(!r.contains(&Point2D::new(80, 215)));
|
||||
assert!(!r.contains(&Point2D::new(-20, 215)));
|
||||
|
||||
// Points beyond the top-left corner.
|
||||
assert!(!r.contains(&Point2D::new(-25, 15)));
|
||||
assert!(!r.contains(&Point2D::new(-15, 10)));
|
||||
|
||||
// Points beyond the top-right corner.
|
||||
assert!(!r.contains(&Point2D::new(85, 20)));
|
||||
assert!(!r.contains(&Point2D::new(75, 10)));
|
||||
|
||||
// Points beyond the bottom-right corner.
|
||||
assert!(!r.contains(&Point2D::new(85, 210)));
|
||||
assert!(!r.contains(&Point2D::new(75, 220)));
|
||||
|
||||
// Points beyond the bottom-left corner.
|
||||
assert!(!r.contains(&Point2D::new(-25, 210)));
|
||||
assert!(!r.contains(&Point2D::new(-15, 220)));
|
||||
|
||||
let r = Rect::new(Point2D::new(-20.0, 15.0), Size2D::new(100.0, 200.0));
|
||||
assert!(r.contains_rect(&r));
|
||||
assert!(!r.contains_rect(&r.translate(&Point2D::new( 0.1, 0.0))));
|
||||
assert!(!r.contains_rect(&r.translate(&Point2D::new(-0.1, 0.0))));
|
||||
assert!(!r.contains_rect(&r.translate(&Point2D::new( 0.0, 0.1))));
|
||||
assert!(!r.contains_rect(&r.translate(&Point2D::new( 0.0, -0.1))));
|
||||
// Empty rectangles are always considered as contained in other rectangles,
|
||||
// even if their origin is not.
|
||||
let p = Point2D::new(1.0, 1.0);
|
||||
assert!(!r.contains(&p));
|
||||
assert!(r.contains_rect(&Rect::new(p, Size2D::zero())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scale() {
|
||||
let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
|
||||
let pp = p.scale(10, 15);
|
||||
|
||||
assert!(pp.size.width == 500);
|
||||
assert!(pp.size.height == 600);
|
||||
assert!(pp.origin.x == 0);
|
||||
assert!(pp.origin.y == 0);
|
||||
|
||||
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
|
||||
let rr = r.scale(1, 20);
|
||||
|
||||
assert!(rr.size.width == 50);
|
||||
assert!(rr.size.height == 800);
|
||||
assert!(rr.origin.x == -10);
|
||||
assert!(rr.origin.y == -100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inflate() {
|
||||
let p = Rect::new(Point2D::new(0, 0), Size2D::new(10, 10));
|
||||
let pp = p.inflate(10, 20);
|
||||
|
||||
assert!(pp.size.width == 30);
|
||||
assert!(pp.size.height == 50);
|
||||
assert!(pp.origin.x == -10);
|
||||
assert!(pp.origin.y == -20);
|
||||
|
||||
let r = Rect::new(Point2D::new(0, 0), Size2D::new(10, 20));
|
||||
let rr = r.inflate(-2, -5);
|
||||
|
||||
assert!(rr.size.width == 6);
|
||||
assert!(rr.size.height == 10);
|
||||
assert!(rr.origin.x == 2);
|
||||
assert!(rr.origin.y == 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_min_max_x_y() {
|
||||
let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
|
||||
assert!(p.max_y() == 40);
|
||||
assert!(p.min_y() == 0);
|
||||
assert!(p.max_x() == 50);
|
||||
assert!(p.min_x() == 0);
|
||||
|
||||
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
|
||||
assert!(r.max_y() == 35);
|
||||
assert!(r.min_y() == -5);
|
||||
assert!(r.max_x() == 40);
|
||||
assert!(r.min_x() == -10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_empty() {
|
||||
assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(0u32, 0u32)).is_empty());
|
||||
assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(10u32, 0u32)).is_empty());
|
||||
assert!(Rect::new(Point2D::new(0u32, 0u32), Size2D::new(0u32, 10u32)).is_empty());
|
||||
assert!(!Rect::new(Point2D::new(0u32, 0u32), Size2D::new(1u32, 1u32)).is_empty());
|
||||
assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(0u32, 0u32)).is_empty());
|
||||
assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(10u32, 0u32)).is_empty());
|
||||
assert!(Rect::new(Point2D::new(10u32, 10u32), Size2D::new(0u32, 10u32)).is_empty());
|
||||
assert!(!Rect::new(Point2D::new(10u32, 10u32), Size2D::new(1u32, 1u32)).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_round() {
|
||||
let mut x = -2.0;
|
||||
let mut y = -2.0;
|
||||
let mut w = -2.0;
|
||||
let mut h = -2.0;
|
||||
while x < 2.0 {
|
||||
while y < 2.0 {
|
||||
while w < 2.0 {
|
||||
while h < 2.0 {
|
||||
let rect = Rect::new(Point2D::new(x, y), Size2D::new(w, h));
|
||||
|
||||
assert!(rect.contains_rect(&rect.round_in()));
|
||||
assert!(rect.round_in().inflate(1.0, 1.0).contains_rect(&rect));
|
||||
|
||||
assert!(rect.round_out().contains_rect(&rect));
|
||||
assert!(rect.inflate(1.0, 1.0).contains_rect(&rect.round_out()));
|
||||
|
||||
assert!(rect.inflate(1.0, 1.0).contains_rect(&rect.round()));
|
||||
assert!(rect.round().inflate(1.0, 1.0).contains_rect(&rect));
|
||||
|
||||
h += 0.1;
|
||||
}
|
||||
w += 0.1;
|
||||
}
|
||||
y += 0.1;
|
||||
}
|
||||
x += 0.1
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
// Copyright 2014 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//! A type-checked scaling factor between units.
|
||||
|
||||
use num::One;
|
||||
|
||||
use heapsize::HeapSizeOf;
|
||||
use num_traits::NumCast;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::fmt;
|
||||
use std::ops::{Add, Mul, Sub, Div};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// A scaling factor between two different units of measurement.
|
||||
///
|
||||
/// This is effectively a type-safe float, intended to be used in combination with other types like
|
||||
/// `length::Length` to enforce conversion between systems of measurement at compile time.
|
||||
///
|
||||
/// `Src` and `Dst` represent the units before and after multiplying a value by a `ScaleFactor`. They
|
||||
/// may be types without values, such as empty enums. For example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use euclid::scale_factor::ScaleFactor;
|
||||
/// use euclid::length::Length;
|
||||
/// enum Mm {};
|
||||
/// enum Inch {};
|
||||
///
|
||||
/// let mm_per_inch: ScaleFactor<f32, Inch, Mm> = ScaleFactor::new(25.4);
|
||||
///
|
||||
/// let one_foot: Length<f32, Inch> = Length::new(12.0);
|
||||
/// let one_foot_in_mm: Length<f32, Mm> = one_foot * mm_per_inch;
|
||||
/// ```
|
||||
#[repr(C)]
|
||||
#[derive(RustcDecodable, RustcEncodable)]
|
||||
pub struct ScaleFactor<T, Src, Dst>(pub T, PhantomData<(Src, Dst)>);
|
||||
|
||||
impl<T: HeapSizeOf, Src, Dst> HeapSizeOf for ScaleFactor<T, Src, Dst> {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.0.heap_size_of_children()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> Deserialize for ScaleFactor<T, Src, Dst> where T: Deserialize {
|
||||
fn deserialize<D>(deserializer: D) -> Result<ScaleFactor<T, Src, Dst>, D::Error>
|
||||
where D: Deserializer {
|
||||
Ok(ScaleFactor(try!(Deserialize::deserialize(deserializer)), PhantomData))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> Serialize for ScaleFactor<T, Src, Dst> where T: Serialize {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
|
||||
self.0.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> ScaleFactor<T, Src, Dst> {
|
||||
pub fn new(x: T) -> ScaleFactor<T, Src, Dst> {
|
||||
ScaleFactor(x, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, Src, Dst> ScaleFactor<T, Src, Dst> {
|
||||
pub fn get(&self) -> T {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + One + Div<T, Output=T>, Src, Dst> ScaleFactor<T, Src, Dst> {
|
||||
/// The inverse ScaleFactor (1.0 / self).
|
||||
pub fn inv(&self) -> ScaleFactor<T, Dst, Src> {
|
||||
let one: T = One::one();
|
||||
ScaleFactor::new(one / self.get())
|
||||
}
|
||||
}
|
||||
|
||||
// scale0 * scale1
|
||||
impl<T: Clone + Mul<T, Output=T>, A, B, C>
|
||||
Mul<ScaleFactor<T, B, C>> for ScaleFactor<T, A, B> {
|
||||
type Output = ScaleFactor<T, A, C>;
|
||||
#[inline]
|
||||
fn mul(self, other: ScaleFactor<T, B, C>) -> ScaleFactor<T, A, C> {
|
||||
ScaleFactor::new(self.get() * other.get())
|
||||
}
|
||||
}
|
||||
|
||||
// scale0 + scale1
|
||||
impl<T: Clone + Add<T, Output=T>, Src, Dst> Add for ScaleFactor<T, Src, Dst> {
|
||||
type Output = ScaleFactor<T, Src, Dst>;
|
||||
#[inline]
|
||||
fn add(self, other: ScaleFactor<T, Src, Dst>) -> ScaleFactor<T, Src, Dst> {
|
||||
ScaleFactor::new(self.get() + other.get())
|
||||
}
|
||||
}
|
||||
|
||||
// scale0 - scale1
|
||||
impl<T: Clone + Sub<T, Output=T>, Src, Dst> Sub for ScaleFactor<T, Src, Dst> {
|
||||
type Output = ScaleFactor<T, Src, Dst>;
|
||||
#[inline]
|
||||
fn sub(self, other: ScaleFactor<T, Src, Dst>) -> ScaleFactor<T, Src, Dst> {
|
||||
ScaleFactor::new(self.get() - other.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Clone, Src, Dst0> ScaleFactor<T, Src, Dst0> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
pub fn cast<T1: NumCast + Clone>(&self) -> Option<ScaleFactor<T1, Src, Dst0>> {
|
||||
NumCast::from(self.get()).map(ScaleFactor::new)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Switch to `derive(PartialEq, Clone)` after this Rust issue is fixed:
|
||||
// https://github.com/mozilla/rust/issues/7671
|
||||
|
||||
impl<T: PartialEq, Src, Dst> PartialEq for ScaleFactor<T, Src, Dst> {
|
||||
fn eq(&self, other: &ScaleFactor<T, Src, Dst>) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, Src, Dst> Clone for ScaleFactor<T, Src, Dst> {
|
||||
fn clone(&self) -> ScaleFactor<T, Src, Dst> {
|
||||
ScaleFactor::new(self.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, Src, Dst> Copy for ScaleFactor<T, Src, Dst> {}
|
||||
|
||||
impl<T: fmt::Debug, Src, Dst> fmt::Debug for ScaleFactor<T, Src, Dst> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, Src, Dst> fmt::Display for ScaleFactor<T, Src, Dst> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ScaleFactor;
|
||||
|
||||
enum Inch {}
|
||||
enum Cm {}
|
||||
enum Mm {}
|
||||
|
||||
#[test]
|
||||
fn test_scale_factor() {
|
||||
let mm_per_inch: ScaleFactor<f32, Inch, Mm> = ScaleFactor::new(25.4);
|
||||
let cm_per_mm: ScaleFactor<f32, Mm, Cm> = ScaleFactor::new(0.1);
|
||||
|
||||
let mm_per_cm: ScaleFactor<f32, Cm, Mm> = cm_per_mm.inv();
|
||||
assert_eq!(mm_per_cm.get(), 10.0);
|
||||
|
||||
let cm_per_inch: ScaleFactor<f32, Inch, Cm> = mm_per_inch * cm_per_mm;
|
||||
assert_eq!(cm_per_inch, ScaleFactor::new(2.54));
|
||||
|
||||
let a: ScaleFactor<isize, Inch, Inch> = ScaleFactor::new(2);
|
||||
let b: ScaleFactor<isize, Inch, Inch> = ScaleFactor::new(3);
|
||||
assert!(a != b);
|
||||
assert_eq!(a, a.clone());
|
||||
assert_eq!(a.clone() + b.clone(), ScaleFactor::new(5));
|
||||
assert_eq!(a - b, ScaleFactor::new(-1));
|
||||
}
|
||||
}
|
|
@ -1,283 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! A group of side offsets, which correspond to top/left/bottom/right for borders, padding,
|
||||
//! and margins in CSS.
|
||||
|
||||
use super::UnknownUnit;
|
||||
use length::Length;
|
||||
use num::Zero;
|
||||
use std::fmt;
|
||||
use std::ops::Add;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
use heapsize::HeapSizeOf;
|
||||
|
||||
/// A group of side offsets, which correspond to top/left/bottom/right for borders, padding,
|
||||
/// and margins in CSS, optionally tagged with a unit.
|
||||
define_matrix! {
|
||||
pub struct TypedSideOffsets2D<T, U> {
|
||||
pub top: T,
|
||||
pub right: T,
|
||||
pub bottom: T,
|
||||
pub left: T,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedSideOffsets2D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({:?},{:?},{:?},{:?})",
|
||||
self.top, self.right, self.bottom, self.left)
|
||||
}
|
||||
}
|
||||
|
||||
/// The default side offset type with no unit.
|
||||
pub type SideOffsets2D<T> = TypedSideOffsets2D<T, UnknownUnit>;
|
||||
|
||||
impl<T: Copy, U> TypedSideOffsets2D<T, U> {
|
||||
/// Constructor taking a scalar for each side.
|
||||
pub fn new(top: T, right: T, bottom: T, left: T) -> TypedSideOffsets2D<T, U> {
|
||||
TypedSideOffsets2D {
|
||||
top: top,
|
||||
right: right,
|
||||
bottom: bottom,
|
||||
left: left,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructor taking a typed Length for each side.
|
||||
pub fn from_lengths(top: Length<T, U>,
|
||||
right: Length<T, U>,
|
||||
bottom: Length<T, U>,
|
||||
left: Length<T, U>) -> TypedSideOffsets2D<T, U> {
|
||||
TypedSideOffsets2D::new(top.0, right.0, bottom.0, left.0)
|
||||
}
|
||||
|
||||
/// Access self.top as a typed Length instead of a scalar value.
|
||||
pub fn top_typed(&self) -> Length<T, U> { Length::new(self.top) }
|
||||
|
||||
/// Access self.right as a typed Length instead of a scalar value.
|
||||
pub fn right_typed(&self) -> Length<T, U> { Length::new(self.right) }
|
||||
|
||||
/// Access self.bottom as a typed Length instead of a scalar value.
|
||||
pub fn bottom_typed(&self) -> Length<T, U> { Length::new(self.bottom) }
|
||||
|
||||
/// Access self.left as a typed Length instead of a scalar value.
|
||||
pub fn left_typed(&self) -> Length<T, U> { Length::new(self.left) }
|
||||
|
||||
/// Constructor setting the same value to all sides, taking a scalar value directly.
|
||||
pub fn new_all_same(all: T) -> TypedSideOffsets2D<T, U> {
|
||||
TypedSideOffsets2D::new(all, all, all, all)
|
||||
}
|
||||
|
||||
/// Constructor setting the same value to all sides, taking a typed Length.
|
||||
pub fn from_length_all_same(all: Length<T, U>) -> TypedSideOffsets2D<T, U> {
|
||||
TypedSideOffsets2D::new_all_same(all.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedSideOffsets2D<T, U> where T: Add<T, Output=T> + Copy {
|
||||
pub fn horizontal(&self) -> T {
|
||||
self.left + self.right
|
||||
}
|
||||
|
||||
pub fn vertical(&self) -> T {
|
||||
self.top + self.bottom
|
||||
}
|
||||
|
||||
pub fn horizontal_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.horizontal())
|
||||
}
|
||||
|
||||
pub fn vertical_typed(&self) -> Length<T, U> {
|
||||
Length::new(self.vertical())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Add for TypedSideOffsets2D<T, U> where T : Copy + Add<T, Output=T> {
|
||||
type Output = TypedSideOffsets2D<T, U>;
|
||||
fn add(self, other: TypedSideOffsets2D<T, U>) -> TypedSideOffsets2D<T, U> {
|
||||
TypedSideOffsets2D::new(
|
||||
self.top + other.top,
|
||||
self.right + other.right,
|
||||
self.bottom + other.bottom,
|
||||
self.left + other.left,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Zero, U> TypedSideOffsets2D<T, U> {
|
||||
/// Constructor, setting all sides to zero.
|
||||
pub fn zero() -> TypedSideOffsets2D<T, U> {
|
||||
TypedSideOffsets2D::new(
|
||||
Zero::zero(),
|
||||
Zero::zero(),
|
||||
Zero::zero(),
|
||||
Zero::zero(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A SIMD enabled version of TypedSideOffsets2D specialized for i32.
|
||||
#[cfg(feature = "unstable")]
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
#[repr(simd)]
|
||||
pub struct SideOffsets2DSimdI32 {
|
||||
pub top: i32,
|
||||
pub bottom: i32,
|
||||
pub right: i32,
|
||||
pub left: i32,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl HeapSizeOf for SideOffsets2DSimdI32 {
|
||||
fn heap_size_of_children(&self) -> usize { 0 }
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl SideOffsets2DSimdI32 {
|
||||
#[inline]
|
||||
pub fn new(top: i32, right: i32, bottom: i32, left: i32) -> SideOffsets2DSimdI32 {
|
||||
SideOffsets2DSimdI32 {
|
||||
top: top,
|
||||
bottom: bottom,
|
||||
right: right,
|
||||
left: left,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl SideOffsets2DSimdI32 {
|
||||
#[inline]
|
||||
pub fn new_all_same(all: i32) -> SideOffsets2DSimdI32 {
|
||||
SideOffsets2DSimdI32::new(all.clone(), all.clone(), all.clone(), all.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl SideOffsets2DSimdI32 {
|
||||
#[inline]
|
||||
pub fn horizontal(&self) -> i32 {
|
||||
self.left + self.right
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn vertical(&self) -> i32 {
|
||||
self.top + self.bottom
|
||||
}
|
||||
}
|
||||
|
||||
/*impl Add for SideOffsets2DSimdI32 {
|
||||
type Output = SideOffsets2DSimdI32;
|
||||
#[inline]
|
||||
fn add(self, other: SideOffsets2DSimdI32) -> SideOffsets2DSimdI32 {
|
||||
self + other // Use SIMD addition
|
||||
}
|
||||
}*/
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
impl SideOffsets2DSimdI32 {
|
||||
#[inline]
|
||||
pub fn zero() -> SideOffsets2DSimdI32 {
|
||||
SideOffsets2DSimdI32 {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
left: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
#[inline]
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.top == 0 && self.right == 0 && self.bottom == 0 && self.left == 0
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[inline]
|
||||
pub fn is_zero(&self) -> bool {
|
||||
let is_zero: bool;
|
||||
unsafe {
|
||||
asm! {
|
||||
"ptest $1, $1
|
||||
setz $0"
|
||||
: "=r"(is_zero)
|
||||
: "x"(*self)
|
||||
:
|
||||
: "intel"
|
||||
};
|
||||
}
|
||||
is_zero
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::SideOffsets2DSimdI32;
|
||||
|
||||
#[test]
|
||||
fn test_is_zero() {
|
||||
assert!(SideOffsets2DSimdI32::new_all_same(0).is_zero());
|
||||
assert!(!SideOffsets2DSimdI32::new_all_same(1).is_zero());
|
||||
assert!(!SideOffsets2DSimdI32::new(1, 0, 0, 0).is_zero());
|
||||
assert!(!SideOffsets2DSimdI32::new(0, 1, 0, 0).is_zero());
|
||||
assert!(!SideOffsets2DSimdI32::new(0, 0, 1, 0).is_zero());
|
||||
assert!(!SideOffsets2DSimdI32::new(0, 0, 0, 1).is_zero());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[cfg(bench)]
|
||||
mod bench {
|
||||
use test::BenchHarness;
|
||||
use std::num::Zero;
|
||||
use rand::{XorShiftRng, Rng};
|
||||
use super::SideOffsets2DSimdI32;
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[bench]
|
||||
fn bench_naive_is_zero(bh: &mut BenchHarness) {
|
||||
fn is_zero(x: &SideOffsets2DSimdI32) -> bool {
|
||||
x.top.is_zero() && x.right.is_zero() && x.bottom.is_zero() && x.left.is_zero()
|
||||
}
|
||||
let mut rng = XorShiftRng::new().unwrap();
|
||||
bh.iter(|| is_zero(&rng.gen::<SideOffsets2DSimdI32>()))
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_is_zero(bh: &mut BenchHarness) {
|
||||
let mut rng = XorShiftRng::new().unwrap();
|
||||
bh.iter(|| rng.gen::<SideOffsets2DSimdI32>().is_zero())
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_naive_add(bh: &mut BenchHarness) {
|
||||
fn add(x: &SideOffsets2DSimdI32, y: &SideOffsets2DSimdI32) -> SideOffsets2DSimdI32 {
|
||||
SideOffsets2DSimdI32 {
|
||||
top: x.top + y.top,
|
||||
right: x.right + y.right,
|
||||
bottom: x.bottom + y.bottom,
|
||||
left: x.left + y.left,
|
||||
}
|
||||
}
|
||||
let mut rng = XorShiftRng::new().unwrap();
|
||||
bh.iter(|| add(&rng.gen::<SideOffsets2DSimdI32>(), &rng.gen::<SideOffsets2DSimdI32>()))
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_add(bh: &mut BenchHarness) {
|
||||
let mut rng = XorShiftRng::new().unwrap();
|
||||
bh.iter(|| rng.gen::<SideOffsets2DSimdI32>() + rng.gen::<SideOffsets2DSimdI32>())
|
||||
}
|
||||
}
|
|
@ -1,276 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::UnknownUnit;
|
||||
use length::Length;
|
||||
use scale_factor::ScaleFactor;
|
||||
use num::*;
|
||||
|
||||
use num_traits::NumCast;
|
||||
use std::fmt;
|
||||
use std::ops::{Add, Div, Mul, Sub};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// A 2d size tagged with a unit.
|
||||
define_matrix! {
|
||||
#[derive(RustcDecodable, RustcEncodable)]
|
||||
pub struct TypedSize2D<T, U> {
|
||||
pub width: T,
|
||||
pub height: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// Default 2d size type with no unit.
|
||||
///
|
||||
/// `Size2D` provides the same methods as `TypedSize2D`.
|
||||
pub type Size2D<T> = TypedSize2D<T, UnknownUnit>;
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedSize2D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}×{:?}", self.width, self.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for TypedSize2D<T, U> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "({}x{})", self.width, self.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedSize2D<T, U> {
|
||||
/// Constructor taking scalar values.
|
||||
pub fn new(width: T, height: T) -> TypedSize2D<T, U> {
|
||||
TypedSize2D {
|
||||
width: width,
|
||||
height: height,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, U> TypedSize2D<T, U> {
|
||||
/// Constructor taking scalar strongly typed lengths.
|
||||
pub fn from_lengths(width: Length<T, U>, height: Length<T, U>) -> TypedSize2D<T, U> {
|
||||
TypedSize2D::new(width.get(), height.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Round, U> TypedSize2D<T, U> {
|
||||
/// Rounds each component to the nearest integer value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
pub fn round(&self) -> Self {
|
||||
TypedSize2D::new(self.width.round(), self.height.round())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ceil, U> TypedSize2D<T, U> {
|
||||
/// Rounds each component to the smallest integer equal or greater than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
pub fn ceil(&self) -> Self {
|
||||
TypedSize2D::new(self.width.ceil(), self.height.ceil())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Floor, U> TypedSize2D<T, U> {
|
||||
/// Rounds each component to the biggest integer equal or lower than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
pub fn floor(&self) -> Self {
|
||||
TypedSize2D::new(self.width.floor(), self.height.floor())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output=T>, U> Add for TypedSize2D<T, U> {
|
||||
type Output = TypedSize2D<T, U>;
|
||||
fn add(self, other: TypedSize2D<T, U>) -> TypedSize2D<T, U> {
|
||||
TypedSize2D::new(self.width + other.width, self.height + other.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output=T>, U> Sub for TypedSize2D<T, U> {
|
||||
type Output = TypedSize2D<T, U>;
|
||||
fn sub(self, other: TypedSize2D<T, U>) -> TypedSize2D<T, U> {
|
||||
TypedSize2D::new(self.width - other.width, self.height - other.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Clone + Mul<T, Output=U>, U> TypedSize2D<T, U> {
|
||||
pub fn area(&self) -> U { self.width * self.height }
|
||||
}
|
||||
|
||||
impl<T: Zero, U> TypedSize2D<T, U> {
|
||||
pub fn zero() -> TypedSize2D<T, U> {
|
||||
TypedSize2D::new(
|
||||
Zero::zero(),
|
||||
Zero::zero(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero, U> Zero for TypedSize2D<T, U> {
|
||||
fn zero() -> TypedSize2D<T, U> {
|
||||
TypedSize2D::new(
|
||||
Zero::zero(),
|
||||
Zero::zero(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output=T>, U> Mul<T> for TypedSize2D<T, U> {
|
||||
type Output = TypedSize2D<T, U>;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> TypedSize2D<T, U> {
|
||||
TypedSize2D::new(self.width * scale, self.height * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U> Div<T> for TypedSize2D<T, U> {
|
||||
type Output = TypedSize2D<T, U>;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> TypedSize2D<T, U> {
|
||||
TypedSize2D::new(self.width / scale, self.height / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output=T>, U1, U2> Mul<ScaleFactor<T, U1, U2>> for TypedSize2D<T, U1> {
|
||||
type Output = TypedSize2D<T, U2>;
|
||||
#[inline]
|
||||
fn mul(self, scale: ScaleFactor<T, U1, U2>) -> TypedSize2D<T, U2> {
|
||||
TypedSize2D::new(self.width * scale.get(), self.height * scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U1, U2> Div<ScaleFactor<T, U1, U2>> for TypedSize2D<T, U2> {
|
||||
type Output = TypedSize2D<T, U1>;
|
||||
#[inline]
|
||||
fn div(self, scale: ScaleFactor<T, U1, U2>) -> TypedSize2D<T, U1> {
|
||||
TypedSize2D::new(self.width / scale.get(), self.height / scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> TypedSize2D<T, U> {
|
||||
/// Returns self.width as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn width_typed(&self) -> Length<T, U> { Length::new(self.width) }
|
||||
|
||||
/// Returns self.height as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn height_typed(&self) -> Length<T, U> { Length::new(self.height) }
|
||||
|
||||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 2] { [self.width, self.height] }
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
pub fn to_untyped(&self) -> Size2D<T> {
|
||||
TypedSize2D::new(self.width, self.height)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
pub fn from_untyped(p: &Size2D<T>) -> TypedSize2D<T, U> {
|
||||
TypedSize2D::new(p.width, p.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Copy, Unit> TypedSize2D<T, Unit> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating point to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
|
||||
pub fn cast<NewT: NumCast + Copy>(&self) -> Option<TypedSize2D<NewT, Unit>> {
|
||||
match (NumCast::from(self.width), NumCast::from(self.height)) {
|
||||
(Some(w), Some(h)) => Some(TypedSize2D::new(w, h)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
|
||||
/// Cast into an `f32` size.
|
||||
pub fn to_f32(&self) -> TypedSize2D<f32, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `uint` size, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point sizes, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_usize(&self) -> TypedSize2D<usize, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `i32` size, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point sizes, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_i32(&self) -> TypedSize2D<i32, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `i64` size, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating point sizes, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
pub fn to_i64(&self) -> TypedSize2D<i64, Unit> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorthand for `TypedSize2D::new(w, h)`.
|
||||
pub fn size2<T, U>(w: T, h: T) -> TypedSize2D<T, U> {
|
||||
TypedSize2D::new(w, h)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod size2d {
|
||||
use super::Size2D;
|
||||
|
||||
#[test]
|
||||
pub fn test_add() {
|
||||
let p1 = Size2D::new(1.0, 2.0);
|
||||
let p2 = Size2D::new(3.0, 4.0);
|
||||
assert_eq!(p1 + p2, Size2D::new(4.0, 6.0));
|
||||
|
||||
let p1 = Size2D::new(1.0, 2.0);
|
||||
let p2 = Size2D::new(0.0, 0.0);
|
||||
assert_eq!(p1 + p2, Size2D::new(1.0, 2.0));
|
||||
|
||||
let p1 = Size2D::new(1.0, 2.0);
|
||||
let p2 = Size2D::new(-3.0, -4.0);
|
||||
assert_eq!(p1 + p2, Size2D::new(-2.0, -2.0));
|
||||
|
||||
let p1 = Size2D::new(0.0, 0.0);
|
||||
let p2 = Size2D::new(0.0, 0.0);
|
||||
assert_eq!(p1 + p2, Size2D::new(0.0, 0.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_sub() {
|
||||
let p1 = Size2D::new(1.0, 2.0);
|
||||
let p2 = Size2D::new(3.0, 4.0);
|
||||
assert_eq!(p1 - p2, Size2D::new(-2.0, -2.0));
|
||||
|
||||
let p1 = Size2D::new(1.0, 2.0);
|
||||
let p2 = Size2D::new(0.0, 0.0);
|
||||
assert_eq!(p1 - p2, Size2D::new(1.0, 2.0));
|
||||
|
||||
let p1 = Size2D::new(1.0, 2.0);
|
||||
let p2 = Size2D::new(-3.0, -4.0);
|
||||
assert_eq!(p1 - p2, Size2D::new(4.0, 6.0));
|
||||
|
||||
let p1 = Size2D::new(0.0, 0.0);
|
||||
let p2 = Size2D::new(0.0, 0.0);
|
||||
assert_eq!(p1 - p2, Size2D::new(0.0, 0.0));
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
|
||||
/// Trait for basic trigonometry functions, so they can be used on generic numeric types
|
||||
pub trait Trig {
|
||||
fn sin(self) -> Self;
|
||||
fn cos(self) -> Self;
|
||||
fn tan(self) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! trig {
|
||||
($ty:ty) => (
|
||||
impl Trig for $ty {
|
||||
#[inline]
|
||||
fn sin(self) -> $ty { self.sin() }
|
||||
#[inline]
|
||||
fn cos(self) -> $ty { self.cos() }
|
||||
#[inline]
|
||||
fn tan(self) -> $ty { self.tan() }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
trig!(f32);
|
||||
trig!(f64);
|
|
@ -1 +1 @@
|
|||
{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"118514fd9c4958df0d25584cda4917186c46011569f55ef350530c1ad3fbdb48",".travis.yml":"56843ecfd2b71797b648b8e537623e84af3c638ea4b8472ed27c55f097bce3dc","COPYRIGHT":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"596d3bcfc1684713b5c557e84b35b98250bebb3d4715e44741d227ab246a16ab","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"625bec69c76ce5423fdd05cfe46922b2680ec517f97c5854ce34798d1d8a9541","src/approxeq.rs":"6cf810ad389c73a27141a7a67454ed12d4b01c3c16605b9a7414b389bc0615dd","src/length.rs":"d7877ebc7ee2e85df2a1f5b9376011bdeeaa5cd7fa2fbdba0934df7456c0821e","src/lib.rs":"77b97c1d7889f037180b68344471ecf7c46b5412fe7c92504debc422c8739b61","src/macros.rs":"b63dabdb52df84ea170dc1dab5fe8d7a78c054562d1566bab416124708d2d7af","src/num.rs":"749b201289fc6663199160a2f9204e17925fd3053f8ab7779e7bfb377ad06227","src/point.rs":"42c1ee0997598d3482ac4c58f255e31b56a97a3a0aa1871b61f55074eecf1ae2","src/rect.rs":"e18811e3be9dba41976b611d52dbe13c5a27dc7db3e3e779daabeed7670b658f","src/scale_factor.rs":"61f979384316ae8a70e836b0d4b016ec5c26a952776037a65801152af4a247cb","src/side_offsets.rs":"fd95ffc9a74e9e84314875c388e763d0780486eb7f9034423e3a22048361e379","src/size.rs":"d9a6fb1f080a06e1332b2e804f8334e086e6d6f17a4288f35133d80b2e2da765","src/transform2d.rs":"4fe4fef7266b06b7790cd400d990ad02e6e605499a1a33c8e39b5e00364389ba","src/transform3d.rs":"cd8a08dd341fcea4c5b10e00d029424e382f3b0002dd8341f302be7f1c12c4fc","src/trig.rs":"ef290927af252ca90a29ba9f17158b591ed591604e66cb9df045dd47b9cfdca5","src/vector.rs":"c087700ad35c3e18e0f5722573f6a24ed2b0452e044c1f0bbb6466c993c560f1"},"package":"995b21c36b37e0f18ed9ba1714378a337e3ff19a6e5e952ea94b0f3dd4e12fbc"}
|
||||
{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"118514fd9c4958df0d25584cda4917186c46011569f55ef350530c1ad3fbdb48",".travis.yml":"13d3e5a7bf83b04c8e8cfa14f0297bd8366d68391d977dd547f64707dffc275a","COPYRIGHT":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"c91c98dc9510ef29a7ce0d7c78294f15cb139c9afecca38e5fda56b0a6984954","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"625bec69c76ce5423fdd05cfe46922b2680ec517f97c5854ce34798d1d8a9541","src/approxeq.rs":"6cf810ad389c73a27141a7a67454ed12d4b01c3c16605b9a7414b389bc0615dd","src/length.rs":"d7c6369f2fe2a17c845b57749bd48c471159f0571a7314d3bf90737d53f697d3","src/lib.rs":"e2e621f05304278d020429d0349acf7a4e7c7a9a72bd23fc0e55680267472ee9","src/macros.rs":"b63dabdb52df84ea170dc1dab5fe8d7a78c054562d1566bab416124708d2d7af","src/matrix2d.rs":"2361338f59813adf4eebaab76e4dd82be0fbfb9ff2461da8dd9ac9d43583b322","src/matrix4d.rs":"b8547bed6108b037192021c97169c00ad456120b849e9b7ac7bec40363edaec1","src/num.rs":"749b201289fc6663199160a2f9204e17925fd3053f8ab7779e7bfb377ad06227","src/point.rs":"dbf12a3ad35dc2502b7f2637856d8ee40f5a96e37ed00f3ee3272bf5752c060c","src/rect.rs":"0a255046dd11a6093d9a77e327e1df31802808536b4d87e4e3b80ff6b208de0f","src/scale_factor.rs":"df6dbd1f0f9f63210b92809f84a383dad982a74f09789cf22c7d8f9b62199d39","src/side_offsets.rs":"f85526a421ffda63ff01a3478d4162c8717eef68e942acfa2fd9a1adee02ebb2","src/size.rs":"ae1b647e300600b50a21dba8c1d915801782ebae82baeb5e49017e6d68a49b28","src/trig.rs":"ef290927af252ca90a29ba9f17158b591ed591604e66cb9df045dd47b9cfdca5"},"package":"6083f113c422ff9cd855a1cf6cc8ec0903606c0eb43a0c6a0ced3bdc9731e4c1"}
|
|
@ -1,9 +1,4 @@
|
|||
language: rust
|
||||
rust:
|
||||
- 1.15.1
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
|
||||
notifications:
|
||||
webhooks: http://build.servo.org:54856/travis
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "euclid"
|
||||
version = "0.14.4"
|
||||
version = "0.13.0"
|
||||
authors = ["The Servo Project Developers"]
|
||||
description = "Geometry primitives"
|
||||
documentation = "https://docs.rs/euclid/"
|
||||
|
@ -14,6 +14,7 @@ unstable = []
|
|||
|
||||
[dependencies]
|
||||
heapsize = "0.4"
|
||||
rustc-serialize = "0.3.2"
|
||||
num-traits = {version = "0.1.32", default-features = false}
|
||||
log = "0.3.1"
|
||||
serde = "0.9"
|
||||
|
|
|
@ -13,7 +13,6 @@ use num::Zero;
|
|||
|
||||
use heapsize::HeapSizeOf;
|
||||
use num_traits::{NumCast, Saturating};
|
||||
use num::One;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::cmp::Ordering;
|
||||
use std::ops::{Add, Sub, Mul, Div, Neg};
|
||||
|
@ -35,6 +34,7 @@ use std::fmt;
|
|||
// Uncomment the derive, and remove the macro call, once heapsize gets
|
||||
// PhantomData<T> support.
|
||||
#[repr(C)]
|
||||
#[derive(RustcDecodable, RustcEncodable)]
|
||||
pub struct Length<T, Unit>(pub T, PhantomData<Unit>);
|
||||
|
||||
impl<T: Clone, Unit> Clone for Length<T, Unit> {
|
||||
|
@ -52,7 +52,7 @@ impl<Unit, T: HeapSizeOf> HeapSizeOf for Length<T, Unit> {
|
|||
}
|
||||
|
||||
impl<Unit, T> Deserialize for Length<T, Unit> where T: Deserialize {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
fn deserialize<D>(deserializer: D) -> Result<Length<T, Unit>,D::Error>
|
||||
where D: Deserializer {
|
||||
Ok(Length(try!(Deserialize::deserialize(deserializer)), PhantomData))
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ impl<T, Unit> Serialize for Length<T, Unit> where T: Serialize {
|
|||
}
|
||||
|
||||
impl<T, Unit> Length<T, Unit> {
|
||||
pub fn new(x: T) -> Self {
|
||||
pub fn new(x: T) -> Length<T, Unit> {
|
||||
Length(x, PhantomData)
|
||||
}
|
||||
}
|
||||
|
@ -173,11 +173,11 @@ impl<Unit, T0: NumCast + Clone> Length<T0, Unit> {
|
|||
}
|
||||
|
||||
impl<Unit, T: Clone + PartialEq> PartialEq for Length<T, Unit> {
|
||||
fn eq(&self, other: &Self) -> bool { self.get().eq(&other.get()) }
|
||||
fn eq(&self, other: &Length<T, Unit>) -> bool { self.get().eq(&other.get()) }
|
||||
}
|
||||
|
||||
impl<Unit, T: Clone + PartialOrd> PartialOrd for Length<T, Unit> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
fn partial_cmp(&self, other: &Length<T, Unit>) -> Option<Ordering> {
|
||||
self.get().partial_cmp(&other.get())
|
||||
}
|
||||
}
|
||||
|
@ -185,27 +185,15 @@ impl<Unit, T: Clone + PartialOrd> PartialOrd for Length<T, Unit> {
|
|||
impl<Unit, T: Clone + Eq> Eq for Length<T, Unit> {}
|
||||
|
||||
impl<Unit, T: Clone + Ord> Ord for Length<T, Unit> {
|
||||
fn cmp(&self, other: &Self) -> Ordering { self.get().cmp(&other.get()) }
|
||||
fn cmp(&self, other: &Length<T, Unit>) -> Ordering { self.get().cmp(&other.get()) }
|
||||
}
|
||||
|
||||
impl<Unit, T: Zero> Zero for Length<T, Unit> {
|
||||
fn zero() -> Self {
|
||||
fn zero() -> Length<T, Unit> {
|
||||
Length::new(Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Length<T, U>
|
||||
where T: Copy + One + Add<Output=T> + Sub<Output=T> + Mul<Output=T> {
|
||||
/// Linearly interpolate between this length and another length.
|
||||
///
|
||||
/// `t` is expected to be between zero and one.
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self {
|
||||
let one_t = T::one() - t;
|
||||
Length::new(one_t * self.get() + t * other.get())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Length;
|
||||
|
|
|
@ -59,6 +59,7 @@ extern crate heapsize;
|
|||
|
||||
#[cfg_attr(test, macro_use)]
|
||||
extern crate log;
|
||||
extern crate rustc_serialize;
|
||||
extern crate serde;
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -69,40 +70,34 @@ extern crate num_traits;
|
|||
|
||||
pub use length::Length;
|
||||
pub use scale_factor::ScaleFactor;
|
||||
pub use transform2d::{Transform2D, TypedTransform2D};
|
||||
pub use transform3d::{Transform3D, TypedTransform3D};
|
||||
pub use matrix2d::{Matrix2D, TypedMatrix2D};
|
||||
pub use matrix4d::{Matrix4D, TypedMatrix4D};
|
||||
pub use point::{
|
||||
Point2D, TypedPoint2D, point2,
|
||||
Point3D, TypedPoint3D, point3,
|
||||
Point2D, TypedPoint2D,
|
||||
Point3D, TypedPoint3D,
|
||||
Point4D, TypedPoint4D,
|
||||
};
|
||||
pub use vector::{
|
||||
Vector2D, TypedVector2D, vec2,
|
||||
Vector3D, TypedVector3D, vec3,
|
||||
};
|
||||
|
||||
pub use rect::{Rect, TypedRect, rect};
|
||||
pub use rect::{Rect, TypedRect};
|
||||
pub use side_offsets::{SideOffsets2D, TypedSideOffsets2D};
|
||||
#[cfg(feature = "unstable")] pub use side_offsets::SideOffsets2DSimdI32;
|
||||
pub use size::{Size2D, TypedSize2D, size2};
|
||||
pub use trig::Trig;
|
||||
pub use size::{Size2D, TypedSize2D};
|
||||
|
||||
pub mod approxeq;
|
||||
pub mod num;
|
||||
mod length;
|
||||
pub mod length;
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod transform2d;
|
||||
mod transform3d;
|
||||
mod point;
|
||||
mod rect;
|
||||
mod scale_factor;
|
||||
mod side_offsets;
|
||||
mod size;
|
||||
mod trig;
|
||||
mod vector;
|
||||
pub mod matrix2d;
|
||||
pub mod matrix4d;
|
||||
pub mod num;
|
||||
pub mod point;
|
||||
pub mod rect;
|
||||
pub mod scale_factor;
|
||||
pub mod side_offsets;
|
||||
pub mod size;
|
||||
pub mod trig;
|
||||
|
||||
/// The default unit.
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, RustcDecodable, RustcEncodable)]
|
||||
pub struct UnknownUnit;
|
||||
|
||||
/// Unit for angles in radians.
|
||||
|
@ -116,20 +111,3 @@ pub type Radians<T> = Length<T, Rad>;
|
|||
|
||||
/// A value in Degrees.
|
||||
pub type Degrees<T> = Length<T, Deg>;
|
||||
|
||||
/// Temporary alias to facilitate the transition to the new naming scheme
|
||||
#[deprecated]
|
||||
pub type Matrix2D<T> = Transform2D<T>;
|
||||
|
||||
/// Temporary alias to facilitate the transition to the new naming scheme
|
||||
#[deprecated]
|
||||
pub type TypedMatrix2D<T, Src, Dst> = TypedTransform2D<T, Src, Dst>;
|
||||
|
||||
/// Temporary alias to facilitate the transition to the new naming scheme
|
||||
#[deprecated]
|
||||
pub type Matrix4D<T> = Transform3D<T>;
|
||||
|
||||
/// Temporary alias to facilitate the transition to the new naming scheme
|
||||
#[deprecated]
|
||||
pub type TypedMatrix4D<T, Src, Dst> = TypedTransform3D<T, Src, Dst>;
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -12,7 +12,6 @@ use length::Length;
|
|||
use scale_factor::ScaleFactor;
|
||||
use num::*;
|
||||
use point::TypedPoint2D;
|
||||
use vector::TypedVector2D;
|
||||
use size::TypedSize2D;
|
||||
|
||||
use heapsize::HeapSizeOf;
|
||||
|
@ -20,10 +19,10 @@ use num_traits::NumCast;
|
|||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::cmp::PartialOrd;
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::{Add, Sub, Mul, Div};
|
||||
|
||||
/// A 2d Rectangle optionally tagged with a unit.
|
||||
#[derive(RustcDecodable, RustcEncodable)]
|
||||
pub struct TypedRect<T, U = UnknownUnit> {
|
||||
pub origin: TypedPoint2D<T, U>,
|
||||
pub size: TypedSize2D<T, U>,
|
||||
|
@ -55,22 +54,14 @@ impl<T: Serialize, U> Serialize for TypedRect<T, U> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Hash, U> Hash for TypedRect<T, U>
|
||||
{
|
||||
fn hash<H: Hasher>(&self, h: &mut H) {
|
||||
self.origin.hash(h);
|
||||
self.size.hash(h);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> Copy for TypedRect<T, U> {}
|
||||
|
||||
impl<T: Copy, U> Clone for TypedRect<T, U> {
|
||||
fn clone(&self) -> Self { *self }
|
||||
fn clone(&self) -> TypedRect<T, U> { *self }
|
||||
}
|
||||
|
||||
impl<T: PartialEq, U> PartialEq<TypedRect<T, U>> for TypedRect<T, U> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
fn eq(&self, other: &TypedRect<T, U>) -> bool {
|
||||
self.origin.eq(&other.origin) && self.size.eq(&other.size)
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +82,7 @@ impl<T: fmt::Display, U> fmt::Display for TypedRect<T, U> {
|
|||
|
||||
impl<T, U> TypedRect<T, U> {
|
||||
/// Constructor.
|
||||
pub fn new(origin: TypedPoint2D<T, U>, size: TypedSize2D<T, U>) -> Self {
|
||||
pub fn new(origin: TypedPoint2D<T, U>, size: TypedSize2D<T, U>) -> TypedRect<T, U> {
|
||||
TypedRect {
|
||||
origin: origin,
|
||||
size: size,
|
||||
|
@ -102,7 +93,7 @@ impl<T, U> TypedRect<T, U> {
|
|||
impl<T, U> TypedRect<T, U>
|
||||
where T: Copy + Clone + Zero + PartialOrd + PartialEq + Add<T, Output=T> + Sub<T, Output=T> {
|
||||
#[inline]
|
||||
pub fn intersects(&self, other: &Self) -> bool {
|
||||
pub fn intersects(&self, other: &TypedRect<T, U>) -> bool {
|
||||
self.origin.x < other.origin.x + other.size.width &&
|
||||
other.origin.x < self.origin.x + self.size.width &&
|
||||
self.origin.y < other.origin.y + other.size.height &&
|
||||
|
@ -150,7 +141,7 @@ where T: Copy + Clone + Zero + PartialOrd + PartialEq + Add<T, Output=T> + Sub<T
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn intersection(&self, other: &Self) -> Option<Self> {
|
||||
pub fn intersection(&self, other: &TypedRect<T, U>) -> Option<TypedRect<T, U>> {
|
||||
if !self.intersects(other) {
|
||||
return None;
|
||||
}
|
||||
|
@ -164,11 +155,13 @@ where T: Copy + Clone + Zero + PartialOrd + PartialEq + Add<T, Output=T> + Sub<T
|
|||
lower_right_y - upper_left.y)))
|
||||
}
|
||||
|
||||
/// Returns the same rectangle, translated by a vector.
|
||||
/// Translates the rect by a vector.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn translate(&self, by: &TypedVector2D<T, U>) -> Self {
|
||||
Self::new(self.origin + *by, self.size)
|
||||
pub fn translate(&self, other: &TypedPoint2D<T, U>) -> TypedRect<T, U> {
|
||||
TypedRect::new(
|
||||
TypedPoint2D::new(self.origin.x + other.x, self.origin.y + other.y),
|
||||
self.size
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns true if this rectangle contains the point. Points are considered
|
||||
|
@ -184,15 +177,14 @@ where T: Copy + Clone + Zero + PartialOrd + PartialEq + Add<T, Output=T> + Sub<T
|
|||
/// returns true if rect is empty, and always returns false if rect is
|
||||
/// nonempty but this rectangle is empty.
|
||||
#[inline]
|
||||
pub fn contains_rect(&self, rect: &Self) -> bool {
|
||||
pub fn contains_rect(&self, rect: &TypedRect<T, U>) -> bool {
|
||||
rect.is_empty() ||
|
||||
(self.min_x() <= rect.min_x() && rect.max_x() <= self.max_x() &&
|
||||
self.min_y() <= rect.min_y() && rect.max_y() <= self.max_y())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inflate(&self, width: T, height: T) -> Self {
|
||||
pub fn inflate(&self, width: T, height: T) -> TypedRect<T, U> {
|
||||
TypedRect::new(
|
||||
TypedPoint2D::new(self.origin.x - width, self.origin.y - height),
|
||||
TypedSize2D::new(self.size.width + width + width, self.size.height + height + height),
|
||||
|
@ -200,8 +192,7 @@ where T: Copy + Clone + Zero + PartialOrd + PartialEq + Add<T, Output=T> + Sub<T
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn inflate_typed(&self, width: Length<T, U>, height: Length<T, U>) -> Self {
|
||||
pub fn inflate_typed(&self, width: Length<T, U>, height: Length<T, U>) -> TypedRect<T, U> {
|
||||
self.inflate(width.get(), height.get())
|
||||
}
|
||||
|
||||
|
@ -221,9 +212,8 @@ where T: Copy + Clone + Zero + PartialOrd + PartialEq + Add<T, Output=T> + Sub<T
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn translate_by_size(&self, size: &TypedSize2D<T, U>) -> Self {
|
||||
self.translate(&size.to_vector())
|
||||
pub fn translate_by_size(&self, size: &TypedSize2D<T, U>) -> TypedRect<T, U> {
|
||||
self.translate(&TypedPoint2D::new(size.width, size.height))
|
||||
}
|
||||
|
||||
/// Returns the smallest rectangle containing the four points.
|
||||
|
@ -252,24 +242,10 @@ where T: Copy + Clone + Zero + PartialOrd + PartialEq + Add<T, Output=T> + Sub<T
|
|||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedRect<T, U>
|
||||
where T: Copy + One + Add<Output=T> + Sub<Output=T> + Mul<Output=T> {
|
||||
/// Linearly interpolate between this rectangle and another rectange.
|
||||
///
|
||||
/// `t` is expected to be between zero and one.
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self {
|
||||
Self::new(
|
||||
self.origin.lerp(other.origin, t),
|
||||
self.size.lerp(other.size, t),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedRect<T, U>
|
||||
where T: Copy + Clone + PartialOrd + Add<T, Output=T> + Sub<T, Output=T> + Zero {
|
||||
#[inline]
|
||||
pub fn union(&self, other: &Self) -> Self {
|
||||
pub fn union(&self, other: &TypedRect<T, U>) -> TypedRect<T, U> {
|
||||
if self.size == Zero::zero() {
|
||||
return *other;
|
||||
}
|
||||
|
@ -292,7 +268,7 @@ where T: Copy + Clone + PartialOrd + Add<T, Output=T> + Sub<T, Output=T> + Zero
|
|||
|
||||
impl<T, U> TypedRect<T, U> {
|
||||
#[inline]
|
||||
pub fn scale<Scale: Copy>(&self, x: Scale, y: Scale) -> Self
|
||||
pub fn scale<Scale: Copy>(&self, x: Scale, y: Scale) -> TypedRect<T, U>
|
||||
where T: Copy + Clone + Mul<Scale, Output=T> {
|
||||
TypedRect::new(
|
||||
TypedPoint2D::new(self.origin.x * x, self.origin.y * y),
|
||||
|
@ -303,9 +279,9 @@ impl<T, U> TypedRect<T, U> {
|
|||
|
||||
impl<T: Copy + PartialEq + Zero, U> TypedRect<T, U> {
|
||||
/// Constructor, setting all sides to zero.
|
||||
pub fn zero() -> Self {
|
||||
pub fn zero() -> TypedRect<T, U> {
|
||||
TypedRect::new(
|
||||
TypedPoint2D::origin(),
|
||||
TypedPoint2D::zero(),
|
||||
TypedSize2D::zero(),
|
||||
)
|
||||
}
|
||||
|
@ -326,17 +302,17 @@ pub fn max<T: Clone + PartialOrd>(x: T, y: T) -> T {
|
|||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output=T>, U> Mul<T> for TypedRect<T, U> {
|
||||
type Output = Self;
|
||||
type Output = TypedRect<T, U>;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self {
|
||||
fn mul(self, scale: T) -> TypedRect<T, U> {
|
||||
TypedRect::new(self.origin * scale, self.size * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U> Div<T> for TypedRect<T, U> {
|
||||
type Output = Self;
|
||||
type Output = TypedRect<T, U>;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self {
|
||||
fn div(self, scale: T) -> TypedRect<T, U> {
|
||||
TypedRect::new(self.origin / scale, self.size / scale)
|
||||
}
|
||||
}
|
||||
|
@ -393,7 +369,6 @@ impl<T: Floor + Ceil + Round + Add<T, Output=T> + Sub<T, Output=T>, U> TypedRect
|
|||
/// avoid pixel rounding errors.
|
||||
/// Note that this is *not* rounding to nearest integer if the values are negative.
|
||||
/// They are always rounding as floor(n + 0.5).
|
||||
#[must_use]
|
||||
pub fn round(&self) -> Self {
|
||||
let origin = self.origin.round();
|
||||
let size = self.origin.add_size(&self.size).round() - origin;
|
||||
|
@ -402,7 +377,6 @@ impl<T: Floor + Ceil + Round + Add<T, Output=T> + Sub<T, Output=T>, U> TypedRect
|
|||
|
||||
/// Return a rectangle with edges rounded to integer coordinates, such that
|
||||
/// the original rectangle contains the resulting rectangle.
|
||||
#[must_use]
|
||||
pub fn round_in(&self) -> Self {
|
||||
let origin = self.origin.ceil();
|
||||
let size = self.origin.add_size(&self.size).floor() - origin;
|
||||
|
@ -411,7 +385,6 @@ impl<T: Floor + Ceil + Round + Add<T, Output=T> + Sub<T, Output=T>, U> TypedRect
|
|||
|
||||
/// Return a rectangle with edges rounded to integer coordinates, such that
|
||||
/// the original rectangle is contained in the resulting rectangle.
|
||||
#[must_use]
|
||||
pub fn round_out(&self) -> Self {
|
||||
let origin = self.origin.floor();
|
||||
let size = self.origin.add_size(&self.size).ceil() - origin;
|
||||
|
@ -462,7 +435,6 @@ pub fn rect<T: Copy, U>(x: T, y: T, w: T, h: T) -> TypedRect<T, U> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use point::Point2D;
|
||||
use vector::vec2;
|
||||
use size::Size2D;
|
||||
use super::*;
|
||||
|
||||
|
@ -478,7 +450,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_translate() {
|
||||
let p = Rect::new(Point2D::new(0u32, 0u32), Size2D::new(50u32, 40u32));
|
||||
let pp = p.translate(&vec2(10,15));
|
||||
let pp = p.translate(&Point2D::new(10,15));
|
||||
|
||||
assert!(pp.size.width == 50);
|
||||
assert!(pp.size.height == 40);
|
||||
|
@ -487,7 +459,7 @@ mod tests {
|
|||
|
||||
|
||||
let r = Rect::new(Point2D::new(-10, -5), Size2D::new(50, 40));
|
||||
let rr = r.translate(&vec2(0,-10));
|
||||
let rr = r.translate(&Point2D::new(0,-10));
|
||||
|
||||
assert!(rr.size.width == 50);
|
||||
assert!(rr.size.height == 40);
|
||||
|
@ -590,10 +562,10 @@ mod tests {
|
|||
|
||||
let r = Rect::new(Point2D::new(-20.0, 15.0), Size2D::new(100.0, 200.0));
|
||||
assert!(r.contains_rect(&r));
|
||||
assert!(!r.contains_rect(&r.translate(&vec2( 0.1, 0.0))));
|
||||
assert!(!r.contains_rect(&r.translate(&vec2(-0.1, 0.0))));
|
||||
assert!(!r.contains_rect(&r.translate(&vec2( 0.0, 0.1))));
|
||||
assert!(!r.contains_rect(&r.translate(&vec2( 0.0, -0.1))));
|
||||
assert!(!r.contains_rect(&r.translate(&Point2D::new( 0.1, 0.0))));
|
||||
assert!(!r.contains_rect(&r.translate(&Point2D::new(-0.1, 0.0))));
|
||||
assert!(!r.contains_rect(&r.translate(&Point2D::new( 0.0, 0.1))));
|
||||
assert!(!r.contains_rect(&r.translate(&Point2D::new( 0.0, -0.1))));
|
||||
// Empty rectangles are always considered as contained in other rectangles,
|
||||
// even if their origin is not.
|
||||
let p = Point2D::new(1.0, 1.0);
|
||||
|
|
|
@ -26,8 +26,8 @@ use std::marker::PhantomData;
|
|||
/// may be types without values, such as empty enums. For example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use euclid::ScaleFactor;
|
||||
/// use euclid::Length;
|
||||
/// use euclid::scale_factor::ScaleFactor;
|
||||
/// use euclid::length::Length;
|
||||
/// enum Mm {};
|
||||
/// enum Inch {};
|
||||
///
|
||||
|
@ -37,6 +37,7 @@ use std::marker::PhantomData;
|
|||
/// let one_foot_in_mm: Length<f32, Mm> = one_foot * mm_per_inch;
|
||||
/// ```
|
||||
#[repr(C)]
|
||||
#[derive(RustcDecodable, RustcEncodable)]
|
||||
pub struct ScaleFactor<T, Src, Dst>(pub T, PhantomData<(Src, Dst)>);
|
||||
|
||||
impl<T: HeapSizeOf, Src, Dst> HeapSizeOf for ScaleFactor<T, Src, Dst> {
|
||||
|
|
|
@ -43,7 +43,7 @@ pub type SideOffsets2D<T> = TypedSideOffsets2D<T, UnknownUnit>;
|
|||
|
||||
impl<T: Copy, U> TypedSideOffsets2D<T, U> {
|
||||
/// Constructor taking a scalar for each side.
|
||||
pub fn new(top: T, right: T, bottom: T, left: T) -> Self {
|
||||
pub fn new(top: T, right: T, bottom: T, left: T) -> TypedSideOffsets2D<T, U> {
|
||||
TypedSideOffsets2D {
|
||||
top: top,
|
||||
right: right,
|
||||
|
@ -57,7 +57,7 @@ impl<T: Copy, U> TypedSideOffsets2D<T, U> {
|
|||
pub fn from_lengths(top: Length<T, U>,
|
||||
right: Length<T, U>,
|
||||
bottom: Length<T, U>,
|
||||
left: Length<T, U>) -> Self {
|
||||
left: Length<T, U>) -> TypedSideOffsets2D<T, U> {
|
||||
TypedSideOffsets2D::new(top.0, right.0, bottom.0, left.0)
|
||||
}
|
||||
|
||||
|
@ -74,12 +74,12 @@ impl<T: Copy, U> TypedSideOffsets2D<T, U> {
|
|||
pub fn left_typed(&self) -> Length<T, U> { Length::new(self.left) }
|
||||
|
||||
/// Constructor setting the same value to all sides, taking a scalar value directly.
|
||||
pub fn new_all_same(all: T) -> Self {
|
||||
pub fn new_all_same(all: T) -> TypedSideOffsets2D<T, U> {
|
||||
TypedSideOffsets2D::new(all, all, all, all)
|
||||
}
|
||||
|
||||
/// Constructor setting the same value to all sides, taking a typed Length.
|
||||
pub fn from_length_all_same(all: Length<T, U>) -> Self {
|
||||
pub fn from_length_all_same(all: Length<T, U>) -> TypedSideOffsets2D<T, U> {
|
||||
TypedSideOffsets2D::new_all_same(all.0)
|
||||
}
|
||||
}
|
||||
|
@ -103,8 +103,8 @@ impl<T, U> TypedSideOffsets2D<T, U> where T: Add<T, Output=T> + Copy {
|
|||
}
|
||||
|
||||
impl<T, U> Add for TypedSideOffsets2D<T, U> where T : Copy + Add<T, Output=T> {
|
||||
type Output = Self;
|
||||
fn add(self, other: Self) -> Self {
|
||||
type Output = TypedSideOffsets2D<T, U>;
|
||||
fn add(self, other: TypedSideOffsets2D<T, U>) -> TypedSideOffsets2D<T, U> {
|
||||
TypedSideOffsets2D::new(
|
||||
self.top + other.top,
|
||||
self.right + other.right,
|
||||
|
@ -116,7 +116,7 @@ impl<T, U> Add for TypedSideOffsets2D<T, U> where T : Copy + Add<T, Output=T> {
|
|||
|
||||
impl<T: Copy + Zero, U> TypedSideOffsets2D<T, U> {
|
||||
/// Constructor, setting all sides to zero.
|
||||
pub fn zero() -> Self {
|
||||
pub fn zero() -> TypedSideOffsets2D<T, U> {
|
||||
TypedSideOffsets2D::new(
|
||||
Zero::zero(),
|
||||
Zero::zero(),
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
use super::UnknownUnit;
|
||||
use length::Length;
|
||||
use scale_factor::ScaleFactor;
|
||||
use vector::{TypedVector2D, vec2};
|
||||
use num::*;
|
||||
|
||||
use num_traits::NumCast;
|
||||
|
@ -20,6 +19,7 @@ use std::marker::PhantomData;
|
|||
|
||||
/// A 2d size tagged with a unit.
|
||||
define_matrix! {
|
||||
#[derive(RustcDecodable, RustcEncodable)]
|
||||
pub struct TypedSize2D<T, U> {
|
||||
pub width: T,
|
||||
pub height: T,
|
||||
|
@ -45,7 +45,7 @@ impl<T: fmt::Display, U> fmt::Display for TypedSize2D<T, U> {
|
|||
|
||||
impl<T, U> TypedSize2D<T, U> {
|
||||
/// Constructor taking scalar values.
|
||||
pub fn new(width: T, height: T) -> Self {
|
||||
pub fn new(width: T, height: T) -> TypedSize2D<T, U> {
|
||||
TypedSize2D {
|
||||
width: width,
|
||||
height: height,
|
||||
|
@ -56,7 +56,7 @@ impl<T, U> TypedSize2D<T, U> {
|
|||
|
||||
impl<T: Clone, U> TypedSize2D<T, U> {
|
||||
/// Constructor taking scalar strongly typed lengths.
|
||||
pub fn from_lengths(width: Length<T, U>, height: Length<T, U>) -> Self {
|
||||
pub fn from_lengths(width: Length<T, U>, height: Length<T, U>) -> TypedSize2D<T, U> {
|
||||
TypedSize2D::new(width.get(), height.get())
|
||||
}
|
||||
}
|
||||
|
@ -89,15 +89,15 @@ impl<T: Floor, U> TypedSize2D<T, U> {
|
|||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output=T>, U> Add for TypedSize2D<T, U> {
|
||||
type Output = Self;
|
||||
fn add(self, other: Self) -> Self {
|
||||
type Output = TypedSize2D<T, U>;
|
||||
fn add(self, other: TypedSize2D<T, U>) -> TypedSize2D<T, U> {
|
||||
TypedSize2D::new(self.width + other.width, self.height + other.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output=T>, U> Sub for TypedSize2D<T, U> {
|
||||
type Output = Self;
|
||||
fn sub(self, other: Self) -> Self {
|
||||
type Output = TypedSize2D<T, U>;
|
||||
fn sub(self, other: TypedSize2D<T, U>) -> TypedSize2D<T, U> {
|
||||
TypedSize2D::new(self.width - other.width, self.height - other.height)
|
||||
}
|
||||
}
|
||||
|
@ -106,23 +106,8 @@ impl<T: Copy + Clone + Mul<T, Output=U>, U> TypedSize2D<T, U> {
|
|||
pub fn area(&self) -> U { self.width * self.height }
|
||||
}
|
||||
|
||||
impl<T, U> TypedSize2D<T, U>
|
||||
where T: Copy + One + Add<Output=T> + Sub<Output=T> + Mul<Output=T> {
|
||||
/// Linearly interpolate between this size and another size.
|
||||
///
|
||||
/// `t` is expected to be between zero and one.
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self {
|
||||
let one_t = T::one() - t;
|
||||
size2(
|
||||
one_t * self.width + t * other.width,
|
||||
one_t * self.height + t * other.height,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zero, U> TypedSize2D<T, U> {
|
||||
pub fn zero() -> Self {
|
||||
pub fn zero() -> TypedSize2D<T, U> {
|
||||
TypedSize2D::new(
|
||||
Zero::zero(),
|
||||
Zero::zero(),
|
||||
|
@ -131,7 +116,7 @@ impl<T: Zero, U> TypedSize2D<T, U> {
|
|||
}
|
||||
|
||||
impl<T: Zero, U> Zero for TypedSize2D<T, U> {
|
||||
fn zero() -> Self {
|
||||
fn zero() -> TypedSize2D<T, U> {
|
||||
TypedSize2D::new(
|
||||
Zero::zero(),
|
||||
Zero::zero(),
|
||||
|
@ -140,17 +125,17 @@ impl<T: Zero, U> Zero for TypedSize2D<T, U> {
|
|||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output=T>, U> Mul<T> for TypedSize2D<T, U> {
|
||||
type Output = Self;
|
||||
type Output = TypedSize2D<T, U>;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self {
|
||||
fn mul(self, scale: T) -> TypedSize2D<T, U> {
|
||||
TypedSize2D::new(self.width * scale, self.height * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U> Div<T> for TypedSize2D<T, U> {
|
||||
type Output = Self;
|
||||
type Output = TypedSize2D<T, U>;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self {
|
||||
fn div(self, scale: T) -> TypedSize2D<T, U> {
|
||||
TypedSize2D::new(self.width / scale, self.height / scale)
|
||||
}
|
||||
}
|
||||
|
@ -183,16 +168,13 @@ impl<T: Copy, U> TypedSize2D<T, U> {
|
|||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 2] { [self.width, self.height] }
|
||||
|
||||
#[inline]
|
||||
pub fn to_vector(&self) -> TypedVector2D<T, U> { vec2(self.width, self.height) }
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
pub fn to_untyped(&self) -> Size2D<T> {
|
||||
TypedSize2D::new(self.width, self.height)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
pub fn from_untyped(p: &Size2D<T>) -> Self {
|
||||
pub fn from_untyped(p: &Size2D<T>) -> TypedSize2D<T, U> {
|
||||
TypedSize2D::new(p.width, p.height)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,488 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::{UnknownUnit, Radians};
|
||||
use num::{One, Zero};
|
||||
use point::TypedPoint2D;
|
||||
use vector::{TypedVector2D, vec2};
|
||||
use rect::TypedRect;
|
||||
use std::ops::{Add, Mul, Div, Sub};
|
||||
use std::marker::PhantomData;
|
||||
use approxeq::ApproxEq;
|
||||
use trig::Trig;
|
||||
use std::fmt;
|
||||
|
||||
define_matrix! {
|
||||
/// A 2d transform stored as a 2 by 3 matrix in row-major order in memory.
|
||||
///
|
||||
/// Transforms can be parametrized over the source and destination units, to describe a
|
||||
/// transformation from a space to another.
|
||||
/// For example, `TypedTransform2D<f32, WordSpace, ScreenSpace>::transform_point4d`
|
||||
/// takes a `TypedPoint2D<f32, WordSpace>` and returns a `TypedPoint2D<f32, ScreenSpace>`.
|
||||
///
|
||||
/// Transforms expose a set of convenience methods for pre- and post-transformations.
|
||||
/// A pre-transformation corresponds to adding an operation that is applied before
|
||||
/// the rest of the transformation, while a post-transformation adds an operation
|
||||
/// that is applied after.
|
||||
pub struct TypedTransform2D<T, Src, Dst> {
|
||||
pub m11: T, pub m12: T,
|
||||
pub m21: T, pub m22: T,
|
||||
pub m31: T, pub m32: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// The default 2d transform type with no units.
|
||||
pub type Transform2D<T> = TypedTransform2D<T, UnknownUnit, UnknownUnit>;
|
||||
|
||||
impl<T: Copy, Src, Dst> TypedTransform2D<T, Src, Dst> {
|
||||
/// Create a transform specifying its matrix elements in row-major order.
|
||||
pub fn row_major(m11: T, m12: T, m21: T, m22: T, m31: T, m32: T) -> Self {
|
||||
TypedTransform2D {
|
||||
m11: m11, m12: m12,
|
||||
m21: m21, m22: m22,
|
||||
m31: m31, m32: m32,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a transform specifying its matrix elements in column-major order.
|
||||
pub fn column_major(m11: T, m21: T, m31: T, m12: T, m22: T, m32: T) -> Self {
|
||||
TypedTransform2D {
|
||||
m11: m11, m12: m12,
|
||||
m21: m21, m22: m22,
|
||||
m31: m31, m32: m32,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an array containing this transform's terms in row-major order (the order
|
||||
/// in which the transform is actually laid out in memory).
|
||||
pub fn to_row_major_array(&self) -> [T; 6] {
|
||||
[
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m31, self.m32
|
||||
]
|
||||
}
|
||||
|
||||
/// Returns an array containing this transform's terms in column-major order.
|
||||
pub fn to_column_major_array(&self) -> [T; 6] {
|
||||
[
|
||||
self.m11, self.m21, self.m31,
|
||||
self.m12, self.m22, self.m32
|
||||
]
|
||||
}
|
||||
|
||||
/// Returns an array containing this transform's 3 rows in (in row-major order)
|
||||
/// as arrays.
|
||||
///
|
||||
/// This is a convenience method to interface with other libraries like glium.
|
||||
pub fn to_row_arrays(&self) -> [[T; 2]; 3] {
|
||||
[
|
||||
[self.m11, self.m12],
|
||||
[self.m21, self.m22],
|
||||
[self.m31, self.m32],
|
||||
]
|
||||
}
|
||||
|
||||
/// Creates a transform from an array of 6 elements in row-major order.
|
||||
pub fn from_row_major_array(array: [T; 6]) -> Self {
|
||||
Self::row_major(
|
||||
array[0], array[1],
|
||||
array[2], array[3],
|
||||
array[4], array[5],
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a transform from 3 rows of 2 elements (row-major order).
|
||||
pub fn from_row_arrays(array: [[T; 2]; 3]) -> Self {
|
||||
Self::row_major(
|
||||
array[0][0], array[0][1],
|
||||
array[1][0], array[1][1],
|
||||
array[2][0], array[2][1],
|
||||
)
|
||||
}
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
pub fn to_untyped(&self) -> Transform2D<T> {
|
||||
Transform2D::row_major(
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m31, self.m32
|
||||
)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
pub fn from_untyped(p: &Transform2D<T>) -> Self {
|
||||
TypedTransform2D::row_major(
|
||||
p.m11, p.m12,
|
||||
p.m21, p.m22,
|
||||
p.m31, p.m32
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> TypedTransform2D<T, Src, Dst>
|
||||
where T: Copy +
|
||||
PartialEq +
|
||||
One + Zero {
|
||||
pub fn identity() -> Self {
|
||||
let (_0, _1) = (Zero::zero(), One::one());
|
||||
TypedTransform2D::row_major(
|
||||
_1, _0,
|
||||
_0, _1,
|
||||
_0, _0
|
||||
)
|
||||
}
|
||||
|
||||
// Intentional not public, because it checks for exact equivalence
|
||||
// while most consumers will probably want some sort of approximate
|
||||
// equivalence to deal with floating-point errors.
|
||||
fn is_identity(&self) -> bool {
|
||||
*self == TypedTransform2D::identity()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> TypedTransform2D<T, Src, Dst>
|
||||
where T: Copy + Clone +
|
||||
Add<T, Output=T> +
|
||||
Mul<T, Output=T> +
|
||||
Div<T, Output=T> +
|
||||
Sub<T, Output=T> +
|
||||
Trig +
|
||||
PartialOrd +
|
||||
One + Zero {
|
||||
|
||||
/// Returns the multiplication of the two matrices such that mat's transformation
|
||||
/// applies after self's transformation.
|
||||
#[must_use]
|
||||
pub fn post_mul<NewDst>(&self, mat: &TypedTransform2D<T, Dst, NewDst>) -> TypedTransform2D<T, Src, NewDst> {
|
||||
TypedTransform2D::row_major(
|
||||
self.m11 * mat.m11 + self.m12 * mat.m21,
|
||||
self.m11 * mat.m12 + self.m12 * mat.m22,
|
||||
self.m21 * mat.m11 + self.m22 * mat.m21,
|
||||
self.m21 * mat.m12 + self.m22 * mat.m22,
|
||||
self.m31 * mat.m11 + self.m32 * mat.m21 + mat.m31,
|
||||
self.m31 * mat.m12 + self.m32 * mat.m22 + mat.m32,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the multiplication of the two matrices such that mat's transformation
|
||||
/// applies before self's transformation.
|
||||
#[must_use]
|
||||
pub fn pre_mul<NewSrc>(&self, mat: &TypedTransform2D<T, NewSrc, Src>) -> TypedTransform2D<T, NewSrc, Dst> {
|
||||
mat.post_mul(self)
|
||||
}
|
||||
|
||||
/// Returns a translation transform.
|
||||
pub fn create_translation(x: T, y: T) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
TypedTransform2D::row_major(
|
||||
_1, _0,
|
||||
_0, _1,
|
||||
x, y
|
||||
)
|
||||
}
|
||||
|
||||
/// Applies a translation after self's transformation and returns the resulting transform.
|
||||
#[must_use]
|
||||
pub fn post_translate(&self, v: TypedVector2D<T, Dst>) -> Self {
|
||||
self.post_mul(&TypedTransform2D::create_translation(v.x, v.y))
|
||||
}
|
||||
|
||||
/// Applies a translation before self's transformation and returns the resulting transform.
|
||||
#[must_use]
|
||||
pub fn pre_translate(&self, v: TypedVector2D<T, Src>) -> Self {
|
||||
self.pre_mul(&TypedTransform2D::create_translation(v.x, v.y))
|
||||
}
|
||||
|
||||
/// Returns a scale transform.
|
||||
pub fn create_scale(x: T, y: T) -> Self {
|
||||
let _0 = Zero::zero();
|
||||
TypedTransform2D::row_major(
|
||||
x, _0,
|
||||
_0, y,
|
||||
_0, _0
|
||||
)
|
||||
}
|
||||
|
||||
/// Applies a scale after self's transformation and returns the resulting transform.
|
||||
#[must_use]
|
||||
pub fn post_scale(&self, x: T, y: T) -> Self {
|
||||
self.post_mul(&TypedTransform2D::create_scale(x, y))
|
||||
}
|
||||
|
||||
/// Applies a scale before self's transformation and returns the resulting transform.
|
||||
#[must_use]
|
||||
pub fn pre_scale(&self, x: T, y: T) -> Self {
|
||||
TypedTransform2D::row_major(
|
||||
self.m11 * x, self.m12,
|
||||
self.m21, self.m22 * y,
|
||||
self.m31, self.m32
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a rotation transform.
|
||||
pub fn create_rotation(theta: Radians<T>) -> Self {
|
||||
let _0 = Zero::zero();
|
||||
let cos = theta.get().cos();
|
||||
let sin = theta.get().sin();
|
||||
TypedTransform2D::row_major(
|
||||
cos, _0 - sin,
|
||||
sin, cos,
|
||||
_0, _0
|
||||
)
|
||||
}
|
||||
|
||||
/// Applies a rotation after self's transformation and returns the resulting transform.
|
||||
#[must_use]
|
||||
pub fn post_rotate(&self, theta: Radians<T>) -> Self {
|
||||
self.post_mul(&TypedTransform2D::create_rotation(theta))
|
||||
}
|
||||
|
||||
/// Applies a rotation after self's transformation and returns the resulting transform.
|
||||
#[must_use]
|
||||
pub fn pre_rotate(&self, theta: Radians<T>) -> Self {
|
||||
self.pre_mul(&TypedTransform2D::create_rotation(theta))
|
||||
}
|
||||
|
||||
/// Returns the given point transformed by this transform.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_point(&self, point: &TypedPoint2D<T, Src>) -> TypedPoint2D<T, Dst> {
|
||||
TypedPoint2D::new(point.x * self.m11 + point.y * self.m21 + self.m31,
|
||||
point.x * self.m12 + point.y * self.m22 + self.m32)
|
||||
}
|
||||
|
||||
/// Returns the given vector transformed by this matrix.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_vector(&self, vec: &TypedVector2D<T, Src>) -> TypedVector2D<T, Dst> {
|
||||
vec2(vec.x * self.m11 + vec.y * self.m21,
|
||||
vec.x * self.m12 + vec.y * self.m22)
|
||||
}
|
||||
|
||||
/// Returns a rectangle that encompasses the result of transforming the given rectangle by this
|
||||
/// transform.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn transform_rect(&self, rect: &TypedRect<T, Src>) -> TypedRect<T, Dst> {
|
||||
TypedRect::from_points(&[
|
||||
self.transform_point(&rect.origin),
|
||||
self.transform_point(&rect.top_right()),
|
||||
self.transform_point(&rect.bottom_left()),
|
||||
self.transform_point(&rect.bottom_right()),
|
||||
])
|
||||
}
|
||||
|
||||
/// Computes and returns the determinant of this transform.
|
||||
pub fn determinant(&self) -> T {
|
||||
self.m11 * self.m22 - self.m12 * self.m21
|
||||
}
|
||||
|
||||
/// Returns the inverse transform if possible.
|
||||
#[must_use]
|
||||
pub fn inverse(&self) -> Option<TypedTransform2D<T, Dst, Src>> {
|
||||
let det = self.determinant();
|
||||
|
||||
let _0: T = Zero::zero();
|
||||
let _1: T = One::one();
|
||||
|
||||
if det == _0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let inv_det = _1 / det;
|
||||
Some(TypedTransform2D::row_major(
|
||||
inv_det * self.m22,
|
||||
inv_det * (_0 - self.m12),
|
||||
inv_det * (_0 - self.m21),
|
||||
inv_det * self.m11,
|
||||
inv_det * (self.m21 * self.m32 - self.m22 * self.m31),
|
||||
inv_det * (self.m31 * self.m12 - self.m11 * self.m32),
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns the same transform with a different destination unit.
|
||||
#[inline]
|
||||
pub fn with_destination<NewDst>(&self) -> TypedTransform2D<T, Src, NewDst> {
|
||||
TypedTransform2D::row_major(
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m31, self.m32,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the same transform with a different source unit.
|
||||
#[inline]
|
||||
pub fn with_source<NewSrc>(&self) -> TypedTransform2D<T, NewSrc, Dst> {
|
||||
TypedTransform2D::row_major(
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m31, self.m32,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ApproxEq<T>, Src, Dst> TypedTransform2D<T, Src, Dst> {
|
||||
pub fn approx_eq(&self, other: &Self) -> bool {
|
||||
self.m11.approx_eq(&other.m11) && self.m12.approx_eq(&other.m12) &&
|
||||
self.m21.approx_eq(&other.m21) && self.m22.approx_eq(&other.m22) &&
|
||||
self.m31.approx_eq(&other.m31) && self.m32.approx_eq(&other.m32)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + fmt::Debug, Src, Dst> fmt::Debug for TypedTransform2D<T, Src, Dst>
|
||||
where T: Copy + fmt::Debug +
|
||||
PartialEq +
|
||||
One + Zero {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.is_identity() {
|
||||
write!(f, "[I]")
|
||||
} else {
|
||||
self.to_row_major_array().fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use approxeq::ApproxEq;
|
||||
use point::Point2D;
|
||||
use Radians;
|
||||
|
||||
use std::f32::consts::FRAC_PI_2;
|
||||
|
||||
type Mat = Transform2D<f32>;
|
||||
|
||||
fn rad(v: f32) -> Radians<f32> { Radians::new(v) }
|
||||
|
||||
#[test]
|
||||
pub fn test_translation() {
|
||||
let t1 = Mat::create_translation(1.0, 2.0);
|
||||
let t2 = Mat::identity().pre_translate(vec2(1.0, 2.0));
|
||||
let t3 = Mat::identity().post_translate(vec2(1.0, 2.0));
|
||||
assert_eq!(t1, t2);
|
||||
assert_eq!(t1, t3);
|
||||
|
||||
assert_eq!(t1.transform_point(&Point2D::new(1.0, 1.0)), Point2D::new(2.0, 3.0));
|
||||
|
||||
assert_eq!(t1.post_mul(&t1), Mat::create_translation(2.0, 4.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_rotation() {
|
||||
let r1 = Mat::create_rotation(rad(FRAC_PI_2));
|
||||
let r2 = Mat::identity().pre_rotate(rad(FRAC_PI_2));
|
||||
let r3 = Mat::identity().post_rotate(rad(FRAC_PI_2));
|
||||
assert_eq!(r1, r2);
|
||||
assert_eq!(r1, r3);
|
||||
|
||||
assert!(r1.transform_point(&Point2D::new(1.0, 2.0)).approx_eq(&Point2D::new(2.0, -1.0)));
|
||||
|
||||
assert!(r1.post_mul(&r1).approx_eq(&Mat::create_rotation(rad(FRAC_PI_2*2.0))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_scale() {
|
||||
let s1 = Mat::create_scale(2.0, 3.0);
|
||||
let s2 = Mat::identity().pre_scale(2.0, 3.0);
|
||||
let s3 = Mat::identity().post_scale(2.0, 3.0);
|
||||
assert_eq!(s1, s2);
|
||||
assert_eq!(s1, s3);
|
||||
|
||||
assert!(s1.transform_point(&Point2D::new(2.0, 2.0)).approx_eq(&Point2D::new(4.0, 6.0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_column_major() {
|
||||
assert_eq!(
|
||||
Mat::row_major(
|
||||
1.0, 2.0,
|
||||
3.0, 4.0,
|
||||
5.0, 6.0
|
||||
),
|
||||
Mat::column_major(
|
||||
1.0, 3.0, 5.0,
|
||||
2.0, 4.0, 6.0,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_simple() {
|
||||
let m1 = Mat::identity();
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.approx_eq(&m2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_scale() {
|
||||
let m1 = Mat::create_scale(1.5, 0.3);
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_mul(&m2).approx_eq(&Mat::identity()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_translate() {
|
||||
let m1 = Mat::create_translation(-132.0, 0.3);
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_mul(&m2).approx_eq(&Mat::identity()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inverse_none() {
|
||||
assert!(Mat::create_scale(2.0, 0.0).inverse().is_none());
|
||||
assert!(Mat::create_scale(2.0, 2.0).inverse().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_pre_post() {
|
||||
let m1 = Transform2D::identity().post_scale(1.0, 2.0).post_translate(vec2(1.0, 2.0));
|
||||
let m2 = Transform2D::identity().pre_translate(vec2(1.0, 2.0)).pre_scale(1.0, 2.0);
|
||||
assert!(m1.approx_eq(&m2));
|
||||
|
||||
let r = Mat::create_rotation(rad(FRAC_PI_2));
|
||||
let t = Mat::create_translation(2.0, 3.0);
|
||||
|
||||
let a = Point2D::new(1.0, 1.0);
|
||||
|
||||
assert!(r.post_mul(&t).transform_point(&a).approx_eq(&Point2D::new(3.0, 2.0)));
|
||||
assert!(t.post_mul(&r).transform_point(&a).approx_eq(&Point2D::new(4.0, -3.0)));
|
||||
assert!(t.post_mul(&r).transform_point(&a).approx_eq(&r.transform_point(&t.transform_point(&a))));
|
||||
|
||||
assert!(r.pre_mul(&t).transform_point(&a).approx_eq(&Point2D::new(4.0, -3.0)));
|
||||
assert!(t.pre_mul(&r).transform_point(&a).approx_eq(&Point2D::new(3.0, 2.0)));
|
||||
assert!(t.pre_mul(&r).transform_point(&a).approx_eq(&t.transform_point(&r.transform_point(&a))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_size_of() {
|
||||
use std::mem::size_of;
|
||||
assert_eq!(size_of::<Transform2D<f32>>(), 6*size_of::<f32>());
|
||||
assert_eq!(size_of::<Transform2D<f64>>(), 6*size_of::<f64>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_identity() {
|
||||
let m1 = Transform2D::identity();
|
||||
assert!(m1.is_identity());
|
||||
let m2 = m1.post_translate(vec2(0.1, 0.0));
|
||||
assert!(!m2.is_identity());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_transform_vector() {
|
||||
// Translation does not apply to vectors.
|
||||
let m1 = Mat::create_translation(1.0, 1.0);
|
||||
let v1 = vec2(10.0, -10.0);
|
||||
assert_eq!(v1, m1.transform_vector(&v1));
|
||||
}
|
||||
}
|
|
@ -1,888 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::{UnknownUnit, Radians};
|
||||
use approxeq::ApproxEq;
|
||||
use trig::Trig;
|
||||
use point::{TypedPoint2D, TypedPoint3D, point2, point3};
|
||||
use vector::{TypedVector2D, TypedVector3D, vec2, vec3};
|
||||
use rect::TypedRect;
|
||||
use transform2d::TypedTransform2D;
|
||||
use scale_factor::ScaleFactor;
|
||||
use num::{One, Zero};
|
||||
use std::ops::{Add, Mul, Sub, Div, Neg};
|
||||
use std::marker::PhantomData;
|
||||
use std::fmt;
|
||||
|
||||
define_matrix! {
|
||||
/// A 3d transform stored as a 4 by 4 matrix in row-major order in memory.
|
||||
///
|
||||
/// Transforms can be parametrized over the source and destination units, to describe a
|
||||
/// transformation from a space to another.
|
||||
/// For example, `TypedTransform3D<f32, WordSpace, ScreenSpace>::transform_point3d`
|
||||
/// takes a `TypedPoint3D<f32, WordSpace>` and returns a `TypedPoint3D<f32, ScreenSpace>`.
|
||||
///
|
||||
/// Transforms expose a set of convenience methods for pre- and post-transformations.
|
||||
/// A pre-transformation corresponds to adding an operation that is applied before
|
||||
/// the rest of the transformation, while a post-transformation adds an operation
|
||||
/// that is applied after.
|
||||
pub struct TypedTransform3D<T, Src, Dst> {
|
||||
pub m11: T, pub m12: T, pub m13: T, pub m14: T,
|
||||
pub m21: T, pub m22: T, pub m23: T, pub m24: T,
|
||||
pub m31: T, pub m32: T, pub m33: T, pub m34: T,
|
||||
pub m41: T, pub m42: T, pub m43: T, pub m44: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// The default 4d transform type with no units.
|
||||
pub type Transform3D<T> = TypedTransform3D<T, UnknownUnit, UnknownUnit>;
|
||||
|
||||
impl<T, Src, Dst> TypedTransform3D<T, Src, Dst> {
|
||||
/// Create a transform specifying its components in row-major order.
|
||||
///
|
||||
/// For example, the translation terms m41, m42, m43 on the last row with the
|
||||
/// row-major convention) are the 13rd, 14th and 15th parameters.
|
||||
#[inline]
|
||||
pub fn row_major(
|
||||
m11: T, m12: T, m13: T, m14: T,
|
||||
m21: T, m22: T, m23: T, m24: T,
|
||||
m31: T, m32: T, m33: T, m34: T,
|
||||
m41: T, m42: T, m43: T, m44: T)
|
||||
-> Self {
|
||||
TypedTransform3D {
|
||||
m11: m11, m12: m12, m13: m13, m14: m14,
|
||||
m21: m21, m22: m22, m23: m23, m24: m24,
|
||||
m31: m31, m32: m32, m33: m33, m34: m34,
|
||||
m41: m41, m42: m42, m43: m43, m44: m44,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a transform specifying its components in column-major order.
|
||||
///
|
||||
/// For example, the translation terms m41, m42, m43 on the last column with the
|
||||
/// column-major convention) are the 4th, 8th and 12nd parameters.
|
||||
#[inline]
|
||||
pub fn column_major(
|
||||
m11: T, m21: T, m31: T, m41: T,
|
||||
m12: T, m22: T, m32: T, m42: T,
|
||||
m13: T, m23: T, m33: T, m43: T,
|
||||
m14: T, m24: T, m34: T, m44: T)
|
||||
-> Self {
|
||||
TypedTransform3D {
|
||||
m11: m11, m12: m12, m13: m13, m14: m14,
|
||||
m21: m21, m22: m22, m23: m23, m24: m24,
|
||||
m31: m31, m32: m32, m33: m33, m34: m34,
|
||||
m41: m41, m42: m42, m43: m43, m44: m44,
|
||||
_unit: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <T, Src, Dst> TypedTransform3D<T, Src, Dst>
|
||||
where T: Copy + Clone +
|
||||
PartialEq +
|
||||
One + Zero {
|
||||
#[inline]
|
||||
pub fn identity() -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
TypedTransform3D::row_major(
|
||||
_1, _0, _0, _0,
|
||||
_0, _1, _0, _0,
|
||||
_0, _0, _1, _0,
|
||||
_0, _0, _0, _1
|
||||
)
|
||||
}
|
||||
|
||||
// Intentional not public, because it checks for exact equivalence
|
||||
// while most consumers will probably want some sort of approximate
|
||||
// equivalence to deal with floating-point errors.
|
||||
#[inline]
|
||||
fn is_identity(&self) -> bool {
|
||||
*self == TypedTransform3D::identity()
|
||||
}
|
||||
}
|
||||
|
||||
impl <T, Src, Dst> TypedTransform3D<T, Src, Dst>
|
||||
where T: Copy + Clone +
|
||||
Add<T, Output=T> +
|
||||
Sub<T, Output=T> +
|
||||
Mul<T, Output=T> +
|
||||
Div<T, Output=T> +
|
||||
Neg<Output=T> +
|
||||
ApproxEq<T> +
|
||||
PartialOrd +
|
||||
Trig +
|
||||
One + Zero {
|
||||
|
||||
/// Create a 4 by 4 transform representing a 2d transformation, specifying its components
|
||||
/// in row-major order.
|
||||
#[inline]
|
||||
pub fn row_major_2d(m11: T, m12: T, m21: T, m22: T, m41: T, m42: T) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
TypedTransform3D::row_major(
|
||||
m11, m12, _0, _0,
|
||||
m21, m22, _0, _0,
|
||||
_0, _0, _1, _0,
|
||||
m41, m42, _0, _1
|
||||
)
|
||||
}
|
||||
|
||||
/// Create an orthogonal projection transform.
|
||||
pub fn ortho(left: T, right: T,
|
||||
bottom: T, top: T,
|
||||
near: T, far: T) -> Self {
|
||||
let tx = -((right + left) / (right - left));
|
||||
let ty = -((top + bottom) / (top - bottom));
|
||||
let tz = -((far + near) / (far - near));
|
||||
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
let _2 = _1 + _1;
|
||||
TypedTransform3D::row_major(
|
||||
_2 / (right - left), _0 , _0 , _0,
|
||||
_0 , _2 / (top - bottom), _0 , _0,
|
||||
_0 , _0 , -_2 / (far - near), _0,
|
||||
tx , ty , tz , _1
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns true if this transform can be represented with a TypedTransform2D.
|
||||
///
|
||||
/// See https://drafts.csswg.org/css-transforms/#2d-transform
|
||||
#[inline]
|
||||
pub fn is_2d(&self) -> bool {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
self.m31 == _0 && self.m32 == _0 &&
|
||||
self.m13 == _0 && self.m23 == _0 &&
|
||||
self.m43 == _0 && self.m14 == _0 &&
|
||||
self.m24 == _0 && self.m34 == _0 &&
|
||||
self.m33 == _1 && self.m44 == _1
|
||||
}
|
||||
|
||||
/// Create a 2D transform picking the relevent terms from this transform.
|
||||
///
|
||||
/// This method assumes that self represents a 2d transformation, callers
|
||||
/// should check that self.is_2d() returns true beforehand.
|
||||
pub fn to_2d(&self) -> TypedTransform2D<T, Src, Dst> {
|
||||
TypedTransform2D::row_major(
|
||||
self.m11, self.m12,
|
||||
self.m21, self.m22,
|
||||
self.m41, self.m42
|
||||
)
|
||||
}
|
||||
|
||||
pub fn approx_eq(&self, other: &Self) -> bool {
|
||||
self.m11.approx_eq(&other.m11) && self.m12.approx_eq(&other.m12) &&
|
||||
self.m13.approx_eq(&other.m13) && self.m14.approx_eq(&other.m14) &&
|
||||
self.m21.approx_eq(&other.m21) && self.m22.approx_eq(&other.m22) &&
|
||||
self.m23.approx_eq(&other.m23) && self.m24.approx_eq(&other.m24) &&
|
||||
self.m31.approx_eq(&other.m31) && self.m32.approx_eq(&other.m32) &&
|
||||
self.m33.approx_eq(&other.m33) && self.m34.approx_eq(&other.m34) &&
|
||||
self.m41.approx_eq(&other.m41) && self.m42.approx_eq(&other.m42) &&
|
||||
self.m43.approx_eq(&other.m43) && self.m44.approx_eq(&other.m44)
|
||||
}
|
||||
|
||||
/// Returns the same transform with a different destination unit.
|
||||
#[inline]
|
||||
pub fn with_destination<NewDst>(&self) -> TypedTransform3D<T, Src, NewDst> {
|
||||
TypedTransform3D::row_major(
|
||||
self.m11, self.m12, self.m13, self.m14,
|
||||
self.m21, self.m22, self.m23, self.m24,
|
||||
self.m31, self.m32, self.m33, self.m34,
|
||||
self.m41, self.m42, self.m43, self.m44,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the same transform with a different source unit.
|
||||
#[inline]
|
||||
pub fn with_source<NewSrc>(&self) -> TypedTransform3D<T, NewSrc, Dst> {
|
||||
TypedTransform3D::row_major(
|
||||
self.m11, self.m12, self.m13, self.m14,
|
||||
self.m21, self.m22, self.m23, self.m24,
|
||||
self.m31, self.m32, self.m33, self.m34,
|
||||
self.m41, self.m42, self.m43, self.m44,
|
||||
)
|
||||
}
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Transform3D<T> {
|
||||
Transform3D::row_major(
|
||||
self.m11, self.m12, self.m13, self.m14,
|
||||
self.m21, self.m22, self.m23, self.m24,
|
||||
self.m31, self.m32, self.m33, self.m34,
|
||||
self.m41, self.m42, self.m43, self.m44,
|
||||
)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(m: &Transform3D<T>) -> Self {
|
||||
TypedTransform3D::row_major(
|
||||
m.m11, m.m12, m.m13, m.m14,
|
||||
m.m21, m.m22, m.m23, m.m24,
|
||||
m.m31, m.m32, m.m33, m.m34,
|
||||
m.m41, m.m42, m.m43, m.m44,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the multiplication of the two matrices such that mat's transformation
|
||||
/// applies after self's transformation.
|
||||
pub fn post_mul<NewDst>(&self, mat: &TypedTransform3D<T, Dst, NewDst>) -> TypedTransform3D<T, Src, NewDst> {
|
||||
TypedTransform3D::row_major(
|
||||
self.m11 * mat.m11 + self.m12 * mat.m21 + self.m13 * mat.m31 + self.m14 * mat.m41,
|
||||
self.m11 * mat.m12 + self.m12 * mat.m22 + self.m13 * mat.m32 + self.m14 * mat.m42,
|
||||
self.m11 * mat.m13 + self.m12 * mat.m23 + self.m13 * mat.m33 + self.m14 * mat.m43,
|
||||
self.m11 * mat.m14 + self.m12 * mat.m24 + self.m13 * mat.m34 + self.m14 * mat.m44,
|
||||
self.m21 * mat.m11 + self.m22 * mat.m21 + self.m23 * mat.m31 + self.m24 * mat.m41,
|
||||
self.m21 * mat.m12 + self.m22 * mat.m22 + self.m23 * mat.m32 + self.m24 * mat.m42,
|
||||
self.m21 * mat.m13 + self.m22 * mat.m23 + self.m23 * mat.m33 + self.m24 * mat.m43,
|
||||
self.m21 * mat.m14 + self.m22 * mat.m24 + self.m23 * mat.m34 + self.m24 * mat.m44,
|
||||
self.m31 * mat.m11 + self.m32 * mat.m21 + self.m33 * mat.m31 + self.m34 * mat.m41,
|
||||
self.m31 * mat.m12 + self.m32 * mat.m22 + self.m33 * mat.m32 + self.m34 * mat.m42,
|
||||
self.m31 * mat.m13 + self.m32 * mat.m23 + self.m33 * mat.m33 + self.m34 * mat.m43,
|
||||
self.m31 * mat.m14 + self.m32 * mat.m24 + self.m33 * mat.m34 + self.m34 * mat.m44,
|
||||
self.m41 * mat.m11 + self.m42 * mat.m21 + self.m43 * mat.m31 + self.m44 * mat.m41,
|
||||
self.m41 * mat.m12 + self.m42 * mat.m22 + self.m43 * mat.m32 + self.m44 * mat.m42,
|
||||
self.m41 * mat.m13 + self.m42 * mat.m23 + self.m43 * mat.m33 + self.m44 * mat.m43,
|
||||
self.m41 * mat.m14 + self.m42 * mat.m24 + self.m43 * mat.m34 + self.m44 * mat.m44,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the multiplication of the two matrices such that mat's transformation
|
||||
/// applies before self's transformation.
|
||||
pub fn pre_mul<NewSrc>(&self, mat: &TypedTransform3D<T, NewSrc, Src>) -> TypedTransform3D<T, NewSrc, Dst> {
|
||||
mat.post_mul(self)
|
||||
}
|
||||
|
||||
/// Returns the inverse transform if possible.
|
||||
pub fn inverse(&self) -> Option<TypedTransform3D<T, Dst, Src>> {
|
||||
let det = self.determinant();
|
||||
|
||||
if det == Zero::zero() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// todo(gw): this could be made faster by special casing
|
||||
// for simpler transform types.
|
||||
let m = TypedTransform3D::row_major(
|
||||
self.m23*self.m34*self.m42 - self.m24*self.m33*self.m42 +
|
||||
self.m24*self.m32*self.m43 - self.m22*self.m34*self.m43 -
|
||||
self.m23*self.m32*self.m44 + self.m22*self.m33*self.m44,
|
||||
|
||||
self.m14*self.m33*self.m42 - self.m13*self.m34*self.m42 -
|
||||
self.m14*self.m32*self.m43 + self.m12*self.m34*self.m43 +
|
||||
self.m13*self.m32*self.m44 - self.m12*self.m33*self.m44,
|
||||
|
||||
self.m13*self.m24*self.m42 - self.m14*self.m23*self.m42 +
|
||||
self.m14*self.m22*self.m43 - self.m12*self.m24*self.m43 -
|
||||
self.m13*self.m22*self.m44 + self.m12*self.m23*self.m44,
|
||||
|
||||
self.m14*self.m23*self.m32 - self.m13*self.m24*self.m32 -
|
||||
self.m14*self.m22*self.m33 + self.m12*self.m24*self.m33 +
|
||||
self.m13*self.m22*self.m34 - self.m12*self.m23*self.m34,
|
||||
|
||||
self.m24*self.m33*self.m41 - self.m23*self.m34*self.m41 -
|
||||
self.m24*self.m31*self.m43 + self.m21*self.m34*self.m43 +
|
||||
self.m23*self.m31*self.m44 - self.m21*self.m33*self.m44,
|
||||
|
||||
self.m13*self.m34*self.m41 - self.m14*self.m33*self.m41 +
|
||||
self.m14*self.m31*self.m43 - self.m11*self.m34*self.m43 -
|
||||
self.m13*self.m31*self.m44 + self.m11*self.m33*self.m44,
|
||||
|
||||
self.m14*self.m23*self.m41 - self.m13*self.m24*self.m41 -
|
||||
self.m14*self.m21*self.m43 + self.m11*self.m24*self.m43 +
|
||||
self.m13*self.m21*self.m44 - self.m11*self.m23*self.m44,
|
||||
|
||||
self.m13*self.m24*self.m31 - self.m14*self.m23*self.m31 +
|
||||
self.m14*self.m21*self.m33 - self.m11*self.m24*self.m33 -
|
||||
self.m13*self.m21*self.m34 + self.m11*self.m23*self.m34,
|
||||
|
||||
self.m22*self.m34*self.m41 - self.m24*self.m32*self.m41 +
|
||||
self.m24*self.m31*self.m42 - self.m21*self.m34*self.m42 -
|
||||
self.m22*self.m31*self.m44 + self.m21*self.m32*self.m44,
|
||||
|
||||
self.m14*self.m32*self.m41 - self.m12*self.m34*self.m41 -
|
||||
self.m14*self.m31*self.m42 + self.m11*self.m34*self.m42 +
|
||||
self.m12*self.m31*self.m44 - self.m11*self.m32*self.m44,
|
||||
|
||||
self.m12*self.m24*self.m41 - self.m14*self.m22*self.m41 +
|
||||
self.m14*self.m21*self.m42 - self.m11*self.m24*self.m42 -
|
||||
self.m12*self.m21*self.m44 + self.m11*self.m22*self.m44,
|
||||
|
||||
self.m14*self.m22*self.m31 - self.m12*self.m24*self.m31 -
|
||||
self.m14*self.m21*self.m32 + self.m11*self.m24*self.m32 +
|
||||
self.m12*self.m21*self.m34 - self.m11*self.m22*self.m34,
|
||||
|
||||
self.m23*self.m32*self.m41 - self.m22*self.m33*self.m41 -
|
||||
self.m23*self.m31*self.m42 + self.m21*self.m33*self.m42 +
|
||||
self.m22*self.m31*self.m43 - self.m21*self.m32*self.m43,
|
||||
|
||||
self.m12*self.m33*self.m41 - self.m13*self.m32*self.m41 +
|
||||
self.m13*self.m31*self.m42 - self.m11*self.m33*self.m42 -
|
||||
self.m12*self.m31*self.m43 + self.m11*self.m32*self.m43,
|
||||
|
||||
self.m13*self.m22*self.m41 - self.m12*self.m23*self.m41 -
|
||||
self.m13*self.m21*self.m42 + self.m11*self.m23*self.m42 +
|
||||
self.m12*self.m21*self.m43 - self.m11*self.m22*self.m43,
|
||||
|
||||
self.m12*self.m23*self.m31 - self.m13*self.m22*self.m31 +
|
||||
self.m13*self.m21*self.m32 - self.m11*self.m23*self.m32 -
|
||||
self.m12*self.m21*self.m33 + self.m11*self.m22*self.m33
|
||||
);
|
||||
|
||||
let _1: T = One::one();
|
||||
Some(m.mul_s(_1 / det))
|
||||
}
|
||||
|
||||
/// Compute the determinant of the transform.
|
||||
pub fn determinant(&self) -> T {
|
||||
self.m14 * self.m23 * self.m32 * self.m41 -
|
||||
self.m13 * self.m24 * self.m32 * self.m41 -
|
||||
self.m14 * self.m22 * self.m33 * self.m41 +
|
||||
self.m12 * self.m24 * self.m33 * self.m41 +
|
||||
self.m13 * self.m22 * self.m34 * self.m41 -
|
||||
self.m12 * self.m23 * self.m34 * self.m41 -
|
||||
self.m14 * self.m23 * self.m31 * self.m42 +
|
||||
self.m13 * self.m24 * self.m31 * self.m42 +
|
||||
self.m14 * self.m21 * self.m33 * self.m42 -
|
||||
self.m11 * self.m24 * self.m33 * self.m42 -
|
||||
self.m13 * self.m21 * self.m34 * self.m42 +
|
||||
self.m11 * self.m23 * self.m34 * self.m42 +
|
||||
self.m14 * self.m22 * self.m31 * self.m43 -
|
||||
self.m12 * self.m24 * self.m31 * self.m43 -
|
||||
self.m14 * self.m21 * self.m32 * self.m43 +
|
||||
self.m11 * self.m24 * self.m32 * self.m43 +
|
||||
self.m12 * self.m21 * self.m34 * self.m43 -
|
||||
self.m11 * self.m22 * self.m34 * self.m43 -
|
||||
self.m13 * self.m22 * self.m31 * self.m44 +
|
||||
self.m12 * self.m23 * self.m31 * self.m44 +
|
||||
self.m13 * self.m21 * self.m32 * self.m44 -
|
||||
self.m11 * self.m23 * self.m32 * self.m44 -
|
||||
self.m12 * self.m21 * self.m33 * self.m44 +
|
||||
self.m11 * self.m22 * self.m33 * self.m44
|
||||
}
|
||||
|
||||
/// Multiplies all of the transform's component by a scalar and returns the result.
|
||||
#[must_use]
|
||||
pub fn mul_s(&self, x: T) -> Self {
|
||||
TypedTransform3D::row_major(
|
||||
self.m11 * x, self.m12 * x, self.m13 * x, self.m14 * x,
|
||||
self.m21 * x, self.m22 * x, self.m23 * x, self.m24 * x,
|
||||
self.m31 * x, self.m32 * x, self.m33 * x, self.m34 * x,
|
||||
self.m41 * x, self.m42 * x, self.m43 * x, self.m44 * x
|
||||
)
|
||||
}
|
||||
|
||||
/// Convenience function to create a scale transform from a ScaleFactor.
|
||||
pub fn from_scale_factor(scale: ScaleFactor<T, Src, Dst>) -> Self {
|
||||
TypedTransform3D::create_scale(scale.get(), scale.get(), scale.get())
|
||||
}
|
||||
|
||||
/// Returns the given 2d point transformed by this transform.
|
||||
///
|
||||
/// The input point must be use the unit Src, and the returned point has the unit Dst.
|
||||
#[inline]
|
||||
pub fn transform_point2d(&self, p: &TypedPoint2D<T, Src>) -> TypedPoint2D<T, Dst> {
|
||||
let x = p.x * self.m11 + p.y * self.m21 + self.m41;
|
||||
let y = p.x * self.m12 + p.y * self.m22 + self.m42;
|
||||
|
||||
let w = p.x * self.m14 + p.y * self.m24 + self.m44;
|
||||
|
||||
point2(x/w, y/w)
|
||||
}
|
||||
|
||||
/// Returns the given 2d vector transformed by this matrix.
|
||||
///
|
||||
/// The input point must be use the unit Src, and the returned point has the unit Dst.
|
||||
#[inline]
|
||||
pub fn transform_vector2d(&self, v: &TypedVector2D<T, Src>) -> TypedVector2D<T, Dst> {
|
||||
vec2(
|
||||
v.x * self.m11 + v.y * self.m21,
|
||||
v.x * self.m12 + v.y * self.m22,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the given 3d point transformed by this transform.
|
||||
///
|
||||
/// The input point must be use the unit Src, and the returned point has the unit Dst.
|
||||
#[inline]
|
||||
pub fn transform_point3d(&self, p: &TypedPoint3D<T, Src>) -> TypedPoint3D<T, Dst> {
|
||||
let x = p.x * self.m11 + p.y * self.m21 + p.z * self.m31 + self.m41;
|
||||
let y = p.x * self.m12 + p.y * self.m22 + p.z * self.m32 + self.m42;
|
||||
let z = p.x * self.m13 + p.y * self.m23 + p.z * self.m33 + self.m43;
|
||||
let w = p.x * self.m14 + p.y * self.m24 + p.z * self.m34 + self.m44;
|
||||
|
||||
point3(x/w, y/w, z/w)
|
||||
}
|
||||
|
||||
/// Returns the given 3d vector transformed by this matrix.
|
||||
///
|
||||
/// The input point must be use the unit Src, and the returned point has the unit Dst.
|
||||
#[inline]
|
||||
pub fn transform_vector3d(&self, v: &TypedVector3D<T, Src>) -> TypedVector3D<T, Dst> {
|
||||
vec3(
|
||||
v.x * self.m11 + v.y * self.m21 + v.z * self.m31,
|
||||
v.x * self.m12 + v.y * self.m22 + v.z * self.m32,
|
||||
v.x * self.m13 + v.y * self.m23 + v.z * self.m33,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a rectangle that encompasses the result of transforming the given rectangle by this
|
||||
/// transform.
|
||||
pub fn transform_rect(&self, rect: &TypedRect<T, Src>) -> TypedRect<T, Dst> {
|
||||
TypedRect::from_points(&[
|
||||
self.transform_point2d(&rect.origin),
|
||||
self.transform_point2d(&rect.top_right()),
|
||||
self.transform_point2d(&rect.bottom_left()),
|
||||
self.transform_point2d(&rect.bottom_right()),
|
||||
])
|
||||
}
|
||||
|
||||
/// Create a 3d translation transform
|
||||
pub fn create_translation(x: T, y: T, z: T) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
TypedTransform3D::row_major(
|
||||
_1, _0, _0, _0,
|
||||
_0, _1, _0, _0,
|
||||
_0, _0, _1, _0,
|
||||
x, y, z, _1
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a transform with a translation applied before self's transformation.
|
||||
#[must_use]
|
||||
pub fn pre_translate(&self, v: TypedVector3D<T, Src>) -> Self {
|
||||
self.pre_mul(&TypedTransform3D::create_translation(v.x, v.y, v.z))
|
||||
}
|
||||
|
||||
/// Returns a transform with a translation applied after self's transformation.
|
||||
#[must_use]
|
||||
pub fn post_translate(&self, v: TypedVector3D<T, Dst>) -> Self {
|
||||
self.post_mul(&TypedTransform3D::create_translation(v.x, v.y, v.z))
|
||||
}
|
||||
|
||||
/// Create a 3d scale transform
|
||||
pub fn create_scale(x: T, y: T, z: T) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
TypedTransform3D::row_major(
|
||||
x, _0, _0, _0,
|
||||
_0, y, _0, _0,
|
||||
_0, _0, z, _0,
|
||||
_0, _0, _0, _1
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a transform with a scale applied before self's transformation.
|
||||
#[must_use]
|
||||
pub fn pre_scale(&self, x: T, y: T, z: T) -> Self {
|
||||
TypedTransform3D::row_major(
|
||||
self.m11 * x, self.m12, self.m13, self.m14,
|
||||
self.m21 , self.m22 * y, self.m23, self.m24,
|
||||
self.m31 , self.m32, self.m33 * z, self.m34,
|
||||
self.m41 , self.m42, self.m43, self.m44
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a transform with a scale applied after self's transformation.
|
||||
#[must_use]
|
||||
pub fn post_scale(&self, x: T, y: T, z: T) -> Self {
|
||||
self.post_mul(&TypedTransform3D::create_scale(x, y, z))
|
||||
}
|
||||
|
||||
/// Create a 3d rotation transform from an angle / axis.
|
||||
/// The supplied axis must be normalized.
|
||||
pub fn create_rotation(x: T, y: T, z: T, theta: Radians<T>) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
let _2 = _1 + _1;
|
||||
|
||||
let xx = x * x;
|
||||
let yy = y * y;
|
||||
let zz = z * z;
|
||||
|
||||
let half_theta = theta.get() / _2;
|
||||
let sc = half_theta.sin() * half_theta.cos();
|
||||
let sq = half_theta.sin() * half_theta.sin();
|
||||
|
||||
TypedTransform3D::row_major(
|
||||
_1 - _2 * (yy + zz) * sq,
|
||||
_2 * (x * y * sq - z * sc),
|
||||
_2 * (x * z * sq + y * sc),
|
||||
_0,
|
||||
|
||||
_2 * (x * y * sq + z * sc),
|
||||
_1 - _2 * (xx + zz) * sq,
|
||||
_2 * (y * z * sq - x * sc),
|
||||
_0,
|
||||
|
||||
_2 * (x * z * sq - y * sc),
|
||||
_2 * (y * z * sq + x * sc),
|
||||
_1 - _2 * (xx + yy) * sq,
|
||||
_0,
|
||||
|
||||
_0,
|
||||
_0,
|
||||
_0,
|
||||
_1
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a transform with a rotation applied after self's transformation.
|
||||
#[must_use]
|
||||
pub fn post_rotate(&self, x: T, y: T, z: T, theta: Radians<T>) -> Self {
|
||||
self.post_mul(&TypedTransform3D::create_rotation(x, y, z, theta))
|
||||
}
|
||||
|
||||
/// Returns a transform with a rotation applied before self's transformation.
|
||||
#[must_use]
|
||||
pub fn pre_rotate(&self, x: T, y: T, z: T, theta: Radians<T>) -> Self {
|
||||
self.pre_mul(&TypedTransform3D::create_rotation(x, y, z, theta))
|
||||
}
|
||||
|
||||
/// Create a 2d skew transform.
|
||||
///
|
||||
/// See https://drafts.csswg.org/css-transforms/#funcdef-skew
|
||||
pub fn create_skew(alpha: Radians<T>, beta: Radians<T>) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
let (sx, sy) = (beta.get().tan(), alpha.get().tan());
|
||||
TypedTransform3D::row_major(
|
||||
_1, sx, _0, _0,
|
||||
sy, _1, _0, _0,
|
||||
_0, _0, _1, _0,
|
||||
_0, _0, _0, _1
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a simple perspective projection transform
|
||||
pub fn create_perspective(d: T) -> Self {
|
||||
let (_0, _1): (T, T) = (Zero::zero(), One::one());
|
||||
TypedTransform3D::row_major(
|
||||
_1, _0, _0, _0,
|
||||
_0, _1, _0, _0,
|
||||
_0, _0, _1, -_1 / d,
|
||||
_0, _0, _0, _1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, Src, Dst> TypedTransform3D<T, Src, Dst> {
|
||||
/// Returns an array containing this transform's terms in row-major order (the order
|
||||
/// in which the transform is actually laid out in memory).
|
||||
pub fn to_row_major_array(&self) -> [T; 16] {
|
||||
[
|
||||
self.m11, self.m12, self.m13, self.m14,
|
||||
self.m21, self.m22, self.m23, self.m24,
|
||||
self.m31, self.m32, self.m33, self.m34,
|
||||
self.m41, self.m42, self.m43, self.m44
|
||||
]
|
||||
}
|
||||
|
||||
/// Returns an array containing this transform's terms in column-major order.
|
||||
pub fn to_column_major_array(&self) -> [T; 16] {
|
||||
[
|
||||
self.m11, self.m21, self.m31, self.m41,
|
||||
self.m12, self.m22, self.m32, self.m42,
|
||||
self.m13, self.m23, self.m33, self.m43,
|
||||
self.m14, self.m24, self.m34, self.m44
|
||||
]
|
||||
}
|
||||
|
||||
/// Returns an array containing this transform's 4 rows in (in row-major order)
|
||||
/// as arrays.
|
||||
///
|
||||
/// This is a convenience method to interface with other libraries like glium.
|
||||
pub fn to_row_arrays(&self) -> [[T; 4]; 4] {
|
||||
[
|
||||
[self.m11, self.m12, self.m13, self.m14],
|
||||
[self.m21, self.m22, self.m23, self.m24],
|
||||
[self.m31, self.m32, self.m33, self.m34],
|
||||
[self.m41, self.m42, self.m43, self.m44]
|
||||
]
|
||||
}
|
||||
|
||||
/// Returns an array containing this transform's 4 columns in (in row-major order,
|
||||
/// or 4 rows in column-major order) as arrays.
|
||||
///
|
||||
/// This is a convenience method to interface with other libraries like glium.
|
||||
pub fn to_column_arrays(&self) -> [[T; 4]; 4] {
|
||||
[
|
||||
[self.m11, self.m21, self.m31, self.m41],
|
||||
[self.m12, self.m22, self.m32, self.m42],
|
||||
[self.m13, self.m23, self.m33, self.m43],
|
||||
[self.m14, self.m24, self.m34, self.m44]
|
||||
]
|
||||
}
|
||||
|
||||
/// Creates a transform from an array of 16 elements in row-major order.
|
||||
pub fn from_array(array: [T; 16]) -> Self {
|
||||
Self::row_major(
|
||||
array[0], array[1], array[2], array[3],
|
||||
array[4], array[5], array[6], array[7],
|
||||
array[8], array[9], array[10], array[11],
|
||||
array[12], array[13], array[14], array[15],
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates a transform from 4 rows of 4 elements (row-major order).
|
||||
pub fn from_row_arrays(array: [[T; 4]; 4]) -> Self {
|
||||
Self::row_major(
|
||||
array[0][0], array[0][1], array[0][2], array[0][3],
|
||||
array[1][0], array[1][1], array[1][2], array[1][3],
|
||||
array[2][0], array[2][1], array[2][2], array[2][3],
|
||||
array[3][0], array[3][1], array[3][2], array[3][3],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Src, Dst> fmt::Debug for TypedTransform3D<T, Src, Dst>
|
||||
where T: Copy + fmt::Debug +
|
||||
PartialEq +
|
||||
One + Zero {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.is_identity() {
|
||||
write!(f, "[I]")
|
||||
} else {
|
||||
self.to_row_major_array().fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use approxeq::ApproxEq;
|
||||
use transform2d::Transform2D;
|
||||
use point::{Point2D, Point3D};
|
||||
use Radians;
|
||||
use super::*;
|
||||
|
||||
use std::f32::consts::FRAC_PI_2;
|
||||
|
||||
type Mf32 = Transform3D<f32>;
|
||||
|
||||
// For convenience.
|
||||
fn rad(v: f32) -> Radians<f32> { Radians::new(v) }
|
||||
|
||||
#[test]
|
||||
pub fn test_translation() {
|
||||
let t1 = Mf32::create_translation(1.0, 2.0, 3.0);
|
||||
let t2 = Mf32::identity().pre_translate(vec3(1.0, 2.0, 3.0));
|
||||
let t3 = Mf32::identity().post_translate(vec3(1.0, 2.0, 3.0));
|
||||
assert_eq!(t1, t2);
|
||||
assert_eq!(t1, t3);
|
||||
|
||||
assert_eq!(t1.transform_point3d(&Point3D::new(1.0, 1.0, 1.0)), Point3D::new(2.0, 3.0, 4.0));
|
||||
assert_eq!(t1.transform_point2d(&Point2D::new(1.0, 1.0)), Point2D::new(2.0, 3.0));
|
||||
|
||||
assert_eq!(t1.post_mul(&t1), Mf32::create_translation(2.0, 4.0, 6.0));
|
||||
|
||||
assert!(!t1.is_2d());
|
||||
assert_eq!(Mf32::create_translation(1.0, 2.0, 3.0).to_2d(), Transform2D::create_translation(1.0, 2.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_rotation() {
|
||||
let r1 = Mf32::create_rotation(0.0, 0.0, 1.0, rad(FRAC_PI_2));
|
||||
let r2 = Mf32::identity().pre_rotate(0.0, 0.0, 1.0, rad(FRAC_PI_2));
|
||||
let r3 = Mf32::identity().post_rotate(0.0, 0.0, 1.0, rad(FRAC_PI_2));
|
||||
assert_eq!(r1, r2);
|
||||
assert_eq!(r1, r3);
|
||||
|
||||
assert!(r1.transform_point3d(&Point3D::new(1.0, 2.0, 3.0)).approx_eq(&Point3D::new(2.0, -1.0, 3.0)));
|
||||
assert!(r1.transform_point2d(&Point2D::new(1.0, 2.0)).approx_eq(&Point2D::new(2.0, -1.0)));
|
||||
|
||||
assert!(r1.post_mul(&r1).approx_eq(&Mf32::create_rotation(0.0, 0.0, 1.0, rad(FRAC_PI_2*2.0))));
|
||||
|
||||
assert!(r1.is_2d());
|
||||
assert!(r1.to_2d().approx_eq(&Transform2D::create_rotation(rad(FRAC_PI_2))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_scale() {
|
||||
let s1 = Mf32::create_scale(2.0, 3.0, 4.0);
|
||||
let s2 = Mf32::identity().pre_scale(2.0, 3.0, 4.0);
|
||||
let s3 = Mf32::identity().post_scale(2.0, 3.0, 4.0);
|
||||
assert_eq!(s1, s2);
|
||||
assert_eq!(s1, s3);
|
||||
|
||||
assert!(s1.transform_point3d(&Point3D::new(2.0, 2.0, 2.0)).approx_eq(&Point3D::new(4.0, 6.0, 8.0)));
|
||||
assert!(s1.transform_point2d(&Point2D::new(2.0, 2.0)).approx_eq(&Point2D::new(4.0, 6.0)));
|
||||
|
||||
assert_eq!(s1.post_mul(&s1), Mf32::create_scale(4.0, 9.0, 16.0));
|
||||
|
||||
assert!(!s1.is_2d());
|
||||
assert_eq!(Mf32::create_scale(2.0, 3.0, 0.0).to_2d(), Transform2D::create_scale(2.0, 3.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_ortho() {
|
||||
let (left, right, bottom, top) = (0.0f32, 1.0f32, 0.1f32, 1.0f32);
|
||||
let (near, far) = (-1.0f32, 1.0f32);
|
||||
let result = Mf32::ortho(left, right, bottom, top, near, far);
|
||||
let expected = Mf32::row_major(
|
||||
2.0, 0.0, 0.0, 0.0,
|
||||
0.0, 2.22222222, 0.0, 0.0,
|
||||
0.0, 0.0, -1.0, 0.0,
|
||||
-1.0, -1.22222222, -0.0, 1.0
|
||||
);
|
||||
debug!("result={:?} expected={:?}", result, expected);
|
||||
assert!(result.approx_eq(&expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_2d() {
|
||||
assert!(Mf32::identity().is_2d());
|
||||
assert!(Mf32::create_rotation(0.0, 0.0, 1.0, rad(0.7854)).is_2d());
|
||||
assert!(!Mf32::create_rotation(0.0, 1.0, 0.0, rad(0.7854)).is_2d());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_row_major_2d() {
|
||||
let m1 = Mf32::row_major_2d(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
|
||||
let m2 = Mf32::row_major(
|
||||
1.0, 2.0, 0.0, 0.0,
|
||||
3.0, 4.0, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
5.0, 6.0, 0.0, 1.0
|
||||
);
|
||||
assert_eq!(m1, m2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_column_major() {
|
||||
assert_eq!(
|
||||
Mf32::row_major(
|
||||
1.0, 2.0, 3.0, 4.0,
|
||||
5.0, 6.0, 7.0, 8.0,
|
||||
9.0, 10.0, 11.0, 12.0,
|
||||
13.0, 14.0, 15.0, 16.0,
|
||||
),
|
||||
Mf32::column_major(
|
||||
1.0, 5.0, 9.0, 13.0,
|
||||
2.0, 6.0, 10.0, 14.0,
|
||||
3.0, 7.0, 11.0, 15.0,
|
||||
4.0, 8.0, 12.0, 16.0,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_simple() {
|
||||
let m1 = Mf32::identity();
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.approx_eq(&m2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_scale() {
|
||||
let m1 = Mf32::create_scale(1.5, 0.3, 2.1);
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_translate() {
|
||||
let m1 = Mf32::create_translation(-132.0, 0.3, 493.0);
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_rotate() {
|
||||
let m1 = Mf32::create_rotation(0.0, 1.0, 0.0, rad(1.57));
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_inverse_transform_point_2d() {
|
||||
let m1 = Mf32::create_translation(100.0, 200.0, 0.0);
|
||||
let m2 = m1.inverse().unwrap();
|
||||
assert!(m1.pre_mul(&m2).approx_eq(&Mf32::identity()));
|
||||
|
||||
let p1 = Point2D::new(1000.0, 2000.0);
|
||||
let p2 = m1.transform_point2d(&p1);
|
||||
assert!(p2.eq(&Point2D::new(1100.0, 2200.0)));
|
||||
|
||||
let p3 = m2.transform_point2d(&p2);
|
||||
assert!(p3.eq(&p1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inverse_none() {
|
||||
assert!(Mf32::create_scale(2.0, 0.0, 2.0).inverse().is_none());
|
||||
assert!(Mf32::create_scale(2.0, 2.0, 2.0).inverse().is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_pre_post() {
|
||||
let m1 = Transform3D::identity().post_scale(1.0, 2.0, 3.0).post_translate(vec3(1.0, 2.0, 3.0));
|
||||
let m2 = Transform3D::identity().pre_translate(vec3(1.0, 2.0, 3.0)).pre_scale(1.0, 2.0, 3.0);
|
||||
assert!(m1.approx_eq(&m2));
|
||||
|
||||
let r = Mf32::create_rotation(0.0, 0.0, 1.0, rad(FRAC_PI_2));
|
||||
let t = Mf32::create_translation(2.0, 3.0, 0.0);
|
||||
|
||||
let a = Point3D::new(1.0, 1.0, 1.0);
|
||||
|
||||
assert!(r.post_mul(&t).transform_point3d(&a).approx_eq(&Point3D::new(3.0, 2.0, 1.0)));
|
||||
assert!(t.post_mul(&r).transform_point3d(&a).approx_eq(&Point3D::new(4.0, -3.0, 1.0)));
|
||||
assert!(t.post_mul(&r).transform_point3d(&a).approx_eq(&r.transform_point3d(&t.transform_point3d(&a))));
|
||||
|
||||
assert!(r.pre_mul(&t).transform_point3d(&a).approx_eq(&Point3D::new(4.0, -3.0, 1.0)));
|
||||
assert!(t.pre_mul(&r).transform_point3d(&a).approx_eq(&Point3D::new(3.0, 2.0, 1.0)));
|
||||
assert!(t.pre_mul(&r).transform_point3d(&a).approx_eq(&t.transform_point3d(&r.transform_point3d(&a))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_size_of() {
|
||||
use std::mem::size_of;
|
||||
assert_eq!(size_of::<Transform3D<f32>>(), 16*size_of::<f32>());
|
||||
assert_eq!(size_of::<Transform3D<f64>>(), 16*size_of::<f64>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_transform_associativity() {
|
||||
let m1 = Mf32::row_major(3.0, 2.0, 1.5, 1.0,
|
||||
0.0, 4.5, -1.0, -4.0,
|
||||
0.0, 3.5, 2.5, 40.0,
|
||||
0.0, 3.0, 0.0, 1.0);
|
||||
let m2 = Mf32::row_major(1.0, -1.0, 3.0, 0.0,
|
||||
-1.0, 0.5, 0.0, 2.0,
|
||||
1.5, -2.0, 6.0, 0.0,
|
||||
-2.5, 6.0, 1.0, 1.0);
|
||||
|
||||
let p = Point3D::new(1.0, 3.0, 5.0);
|
||||
let p1 = m2.pre_mul(&m1).transform_point3d(&p);
|
||||
let p2 = m2.transform_point3d(&m1.transform_point3d(&p));
|
||||
assert!(p1.approx_eq(&p2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_identity() {
|
||||
let m1 = Transform3D::identity();
|
||||
assert!(m1.is_identity());
|
||||
let m2 = m1.post_translate(vec3(0.1, 0.0, 0.0));
|
||||
assert!(!m2.is_identity());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_transform_vector() {
|
||||
// Translation does not apply to vectors.
|
||||
let m = Mf32::create_translation(1.0, 2.0, 3.0);
|
||||
let v1 = vec3(10.0, -10.0, 3.0);
|
||||
assert_eq!(v1, m.transform_vector3d(&v1));
|
||||
// While it does apply to points.
|
||||
assert!(v1.to_point() != m.transform_point3d(&v1.to_point()));
|
||||
|
||||
// same thing with 2d vectors/points
|
||||
let v2 = vec2(10.0, -5.0);
|
||||
assert_eq!(v2, m.transform_vector2d(&v2));
|
||||
assert!(v2.to_point() != m.transform_point2d(&v2.to_point()));
|
||||
}
|
||||
}
|
|
@ -1,894 +0,0 @@
|
|||
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::UnknownUnit;
|
||||
use approxeq::ApproxEq;
|
||||
use length::Length;
|
||||
use point::{TypedPoint2D, TypedPoint3D, point2, point3};
|
||||
use size::{TypedSize2D, size2};
|
||||
use scale_factor::ScaleFactor;
|
||||
use num::*;
|
||||
use num_traits::{Float, NumCast};
|
||||
use std::fmt;
|
||||
use std::ops::{Add, Neg, Mul, Sub, Div, AddAssign, SubAssign, MulAssign, DivAssign};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
define_matrix! {
|
||||
/// A 2d Vector tagged with a unit.
|
||||
pub struct TypedVector2D<T, U> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// Default 2d vector type with no unit.
|
||||
///
|
||||
/// `Vector2D` provides the same methods as `TypedVector2D`.
|
||||
pub type Vector2D<T> = TypedVector2D<T, UnknownUnit>;
|
||||
|
||||
impl<T: Copy + Zero, U> TypedVector2D<T, U> {
|
||||
/// Constructor, setting all components to zero.
|
||||
#[inline]
|
||||
pub fn zero() -> Self {
|
||||
TypedVector2D::new(Zero::zero(), Zero::zero())
|
||||
}
|
||||
|
||||
/// Convert into a 3d vector.
|
||||
#[inline]
|
||||
pub fn to_3d(&self) -> TypedVector3D<T, U> {
|
||||
vec3(self.x, self.y, Zero::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedVector2D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({:?},{:?})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for TypedVector2D<T, U> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "({},{})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> TypedVector2D<T, U> {
|
||||
/// Constructor taking scalar values directly.
|
||||
#[inline]
|
||||
pub fn new(x: T, y: T) -> Self {
|
||||
TypedVector2D { x: x, y: y, _unit: PhantomData }
|
||||
}
|
||||
|
||||
/// Constructor taking properly typed Lengths instead of scalar values.
|
||||
#[inline]
|
||||
pub fn from_lengths(x: Length<T, U>, y: Length<T, U>) -> Self {
|
||||
vec2(x.0, y.0)
|
||||
}
|
||||
|
||||
/// Create a 3d vector from this one, using the specified z value.
|
||||
#[inline]
|
||||
pub fn extend(&self, z: T) -> TypedVector3D<T, U> {
|
||||
vec3(self.x, self.y, z)
|
||||
}
|
||||
|
||||
/// Cast this vector into a point.
|
||||
///
|
||||
/// Equivalent to adding this vector to the origin.
|
||||
#[inline]
|
||||
pub fn to_point(&self) -> TypedPoint2D<T, U> {
|
||||
point2(self.x, self.y)
|
||||
}
|
||||
|
||||
/// Cast this vector into a size.
|
||||
#[inline]
|
||||
pub fn to_size(&self) -> TypedSize2D<T, U> {
|
||||
size2(self.x, self.y)
|
||||
}
|
||||
|
||||
|
||||
/// Returns self.x as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn x_typed(&self) -> Length<T, U> { Length::new(self.x) }
|
||||
|
||||
/// Returns self.y as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn y_typed(&self) -> Length<T, U> { Length::new(self.y) }
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Vector2D<T> {
|
||||
vec2(self.x, self.y)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(p: &Vector2D<T>) -> Self {
|
||||
vec2(p.x, p.y)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 2] {
|
||||
[self.x, self.y]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedVector2D<T, U>
|
||||
where T: Copy + Mul<T, Output=T> + Add<T, Output=T> + Sub<T, Output=T> {
|
||||
/// Dot product.
|
||||
#[inline]
|
||||
pub fn dot(self, other: Self) -> T {
|
||||
self.x * other.x + self.y * other.y
|
||||
}
|
||||
|
||||
/// Returns the norm of the cross product [self.x, self.y, 0] x [other.x, other.y, 0]..
|
||||
#[inline]
|
||||
pub fn cross(self, other: Self) -> T {
|
||||
self.x * other.y - self.y * other.x
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn normalize(self) -> Self where T: Float + ApproxEq<T> {
|
||||
let dot = self.dot(self);
|
||||
if dot.approx_eq(&T::zero()) {
|
||||
self
|
||||
} else {
|
||||
self / dot.sqrt()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn square_length(&self) -> T {
|
||||
self.x * self.x + self.y * self.y
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn length(&self) -> T where T: Float + ApproxEq<T> {
|
||||
self.square_length().sqrt()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedVector2D<T, U>
|
||||
where T: Copy + One + Add<Output=T> + Sub<Output=T> + Mul<Output=T> {
|
||||
/// Linearly interpolate between this vector and another vector.
|
||||
///
|
||||
/// `t` is expected to be between zero and one.
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self {
|
||||
let one_t = T::one() - t;
|
||||
(*self) * one_t + other * t
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output=T>, U> Add for TypedVector2D<T, U> {
|
||||
type Output = Self;
|
||||
fn add(self, other: Self) -> Self {
|
||||
TypedVector2D::new(self.x + other.x, self.y + other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output=T>, U> AddAssign for TypedVector2D<T, U> {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
*self = *self + other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output=T>, U> SubAssign<TypedVector2D<T, U>> for TypedVector2D<T, U> {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: Self) {
|
||||
*self = *self - other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output=T>, U> Sub for TypedVector2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn sub(self, other: Self) -> Self {
|
||||
vec2(self.x - other.x, self.y - other.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: Copy + Neg<Output=T>, U> Neg for TypedVector2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn neg(self) -> Self {
|
||||
vec2(-self.x, -self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float, U> TypedVector2D<T, U> {
|
||||
#[inline]
|
||||
pub fn min(self, other: Self) -> Self {
|
||||
vec2(self.x.min(other.x), self.y.min(other.y))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max(self, other: Self) -> Self {
|
||||
vec2(self.x.max(other.x), self.y.max(other.y))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output=T>, U> Mul<T> for TypedVector2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self {
|
||||
vec2(self.x * scale, self.y * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U> Div<T> for TypedVector2D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self {
|
||||
vec2(self.x / scale, self.y / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output=T>, U> MulAssign<T> for TypedVector2D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: T) {
|
||||
*self = *self * scale
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U> DivAssign<T> for TypedVector2D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: T) {
|
||||
*self = *self / scale
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output=T>, U1, U2> Mul<ScaleFactor<T, U1, U2>> for TypedVector2D<T, U1> {
|
||||
type Output = TypedVector2D<T, U2>;
|
||||
#[inline]
|
||||
fn mul(self, scale: ScaleFactor<T, U1, U2>) -> TypedVector2D<T, U2> {
|
||||
vec2(self.x * scale.get(), self.y * scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U1, U2> Div<ScaleFactor<T, U1, U2>> for TypedVector2D<T, U2> {
|
||||
type Output = TypedVector2D<T, U1>;
|
||||
#[inline]
|
||||
fn div(self, scale: ScaleFactor<T, U1, U2>) -> TypedVector2D<T, U1> {
|
||||
vec2(self.x / scale.get(), self.y / scale.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Round, U> TypedVector2D<T, U> {
|
||||
/// Rounds each component to the nearest integer value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
/// For example `{ -0.1, -0.8 }.round() == { 0.0, -1.0 }`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn round(&self) -> Self {
|
||||
vec2(self.x.round(), self.y.round())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ceil, U> TypedVector2D<T, U> {
|
||||
/// Rounds each component to the smallest integer equal or greater than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
/// For example `{ -0.1, -0.8 }.ceil() == { 0.0, 0.0 }`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn ceil(&self) -> Self {
|
||||
vec2(self.x.ceil(), self.y.ceil())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Floor, U> TypedVector2D<T, U> {
|
||||
/// Rounds each component to the biggest integer equal or lower than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
/// For example `{ -0.1, -0.8 }.floor() == { -1.0, -1.0 }`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn floor(&self) -> Self {
|
||||
vec2(self.x.floor(), self.y.floor())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Copy, U> TypedVector2D<T, U> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating vector to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using `round()`, `ceil()` or `floor()` before casting.
|
||||
#[inline]
|
||||
pub fn cast<NewT: NumCast + Copy>(&self) -> Option<TypedVector2D<NewT, U>> {
|
||||
match (NumCast::from(self.x), NumCast::from(self.y)) {
|
||||
(Some(x), Some(y)) => Some(TypedVector2D::new(x, y)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
|
||||
/// Cast into an `f32` vector.
|
||||
#[inline]
|
||||
pub fn to_f32(&self) -> TypedVector2D<f32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `usize` vector, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating vector vectors, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_usize(&self) -> TypedVector2D<usize, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an i32 vector, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating vector vectors, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i32(&self) -> TypedVector2D<i32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an i64 vector, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating vector vectors, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i64(&self) -> TypedVector2D<i64, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy+ApproxEq<T>, U> ApproxEq<TypedVector2D<T, U>> for TypedVector2D<T, U> {
|
||||
#[inline]
|
||||
fn approx_epsilon() -> Self {
|
||||
vec2(T::approx_epsilon(), T::approx_epsilon())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn approx_eq(&self, other: &Self) -> bool {
|
||||
self.x.approx_eq(&other.x) && self.y.approx_eq(&other.y)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn approx_eq_eps(&self, other: &Self, eps: &Self) -> bool {
|
||||
self.x.approx_eq_eps(&other.x, &eps.x) && self.y.approx_eq_eps(&other.y, &eps.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> Into<[T; 2]> for TypedVector2D<T, U> {
|
||||
fn into(self) -> [T; 2] {
|
||||
self.to_array()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> From<[T; 2]> for TypedVector2D<T, U> {
|
||||
fn from(array: [T; 2]) -> Self {
|
||||
vec2(array[0], array[1])
|
||||
}
|
||||
}
|
||||
|
||||
define_matrix! {
|
||||
/// A 3d Vector tagged with a unit.
|
||||
pub struct TypedVector3D<T, U> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
pub z: T,
|
||||
}
|
||||
}
|
||||
|
||||
/// Default 3d vector type with no unit.
|
||||
///
|
||||
/// `Vector3D` provides the same methods as `TypedVector3D`.
|
||||
pub type Vector3D<T> = TypedVector3D<T, UnknownUnit>;
|
||||
|
||||
impl<T: Copy + Zero, U> TypedVector3D<T, U> {
|
||||
/// Constructor, setting all copmonents to zero.
|
||||
#[inline]
|
||||
pub fn zero() -> Self {
|
||||
vec3(Zero::zero(), Zero::zero(), Zero::zero())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_array_4d(&self) -> [T; 4] {
|
||||
[self.x, self.y, self.z, Zero::zero()]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug, U> fmt::Debug for TypedVector3D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({:?},{:?},{:?})", self.x, self.y, self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, U> fmt::Display for TypedVector3D<T, U> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "({},{},{})", self.x, self.y, self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> TypedVector3D<T, U> {
|
||||
/// Constructor taking scalar values directly.
|
||||
#[inline]
|
||||
pub fn new(x: T, y: T, z: T) -> Self {
|
||||
TypedVector3D { x: x, y: y, z: z, _unit: PhantomData }
|
||||
}
|
||||
|
||||
/// Constructor taking properly typed Lengths instead of scalar values.
|
||||
#[inline]
|
||||
pub fn from_lengths(x: Length<T, U>, y: Length<T, U>, z: Length<T, U>) -> TypedVector3D<T, U> {
|
||||
vec3(x.0, y.0, z.0)
|
||||
}
|
||||
|
||||
/// Cast this vector into a point.
|
||||
///
|
||||
/// Equivalent to adding this vector to the origin.
|
||||
#[inline]
|
||||
pub fn to_point(&self) -> TypedPoint3D<T, U> {
|
||||
point3(self.x, self.y, self.z)
|
||||
}
|
||||
|
||||
/// Returns self.x as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn x_typed(&self) -> Length<T, U> { Length::new(self.x) }
|
||||
|
||||
/// Returns self.y as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn y_typed(&self) -> Length<T, U> { Length::new(self.y) }
|
||||
|
||||
/// Returns self.z as a Length carrying the unit.
|
||||
#[inline]
|
||||
pub fn z_typed(&self) -> Length<T, U> { Length::new(self.z) }
|
||||
|
||||
#[inline]
|
||||
pub fn to_array(&self) -> [T; 3] { [self.x, self.y, self.z] }
|
||||
|
||||
/// Drop the units, preserving only the numeric value.
|
||||
#[inline]
|
||||
pub fn to_untyped(&self) -> Vector3D<T> {
|
||||
vec3(self.x, self.y, self.z)
|
||||
}
|
||||
|
||||
/// Tag a unitless value with units.
|
||||
#[inline]
|
||||
pub fn from_untyped(p: &Vector3D<T>) -> Self {
|
||||
vec3(p.x, p.y, p.z)
|
||||
}
|
||||
|
||||
/// Convert into a 2d vector.
|
||||
#[inline]
|
||||
pub fn to_2d(&self) -> TypedVector2D<T, U> {
|
||||
vec2(self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Mul<T, Output=T> +
|
||||
Add<T, Output=T> +
|
||||
Sub<T, Output=T> +
|
||||
Copy, U> TypedVector3D<T, U> {
|
||||
|
||||
// Dot product.
|
||||
#[inline]
|
||||
pub fn dot(self, other: Self) -> T {
|
||||
self.x * other.x +
|
||||
self.y * other.y +
|
||||
self.z * other.z
|
||||
}
|
||||
|
||||
// Cross product.
|
||||
#[inline]
|
||||
pub fn cross(self, other: Self) -> Self {
|
||||
vec3(
|
||||
self.y * other.z - self.z * other.y,
|
||||
self.z * other.x - self.x * other.z,
|
||||
self.x * other.y - self.y * other.x
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn normalize(self) -> Self where T: Float + ApproxEq<T> {
|
||||
let dot = self.dot(self);
|
||||
if dot.approx_eq(&T::zero()) {
|
||||
self
|
||||
} else {
|
||||
self / dot.sqrt()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn square_length(&self) -> T {
|
||||
self.x * self.x + self.y * self.y + self.z * self.z
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn length(&self) -> T where T: Float + ApproxEq<T> {
|
||||
self.square_length().sqrt()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> TypedVector3D<T, U>
|
||||
where T: Copy + One + Add<Output=T> + Sub<Output=T> + Mul<Output=T> {
|
||||
/// Linearly interpolate between this vector and another vector.
|
||||
///
|
||||
/// `t` is expected to be between zero and one.
|
||||
#[inline]
|
||||
pub fn lerp(&self, other: Self, t: T) -> Self {
|
||||
let one_t = T::one() - t;
|
||||
(*self) * one_t + other * t
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output=T>, U> Add for TypedVector3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn add(self, other: Self) -> Self {
|
||||
vec3(self.x + other.x, self.y + other.y, self.z + other.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output=T>, U> Sub for TypedVector3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn sub(self, other: Self) -> Self {
|
||||
vec3(self.x - other.x, self.y - other.y, self.z - other.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Add<T, Output=T>, U> AddAssign for TypedVector3D<T, U> {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
*self = *self + other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Sub<T, Output=T>, U> SubAssign<TypedVector3D<T, U>> for TypedVector3D<T, U> {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: Self) {
|
||||
*self = *self - other
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: Copy + Neg<Output=T>, U> Neg for TypedVector3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn neg(self) -> Self {
|
||||
vec3(-self.x, -self.y, -self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output=T>, U> Mul<T> for TypedVector3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn mul(self, scale: T) -> Self {
|
||||
Self::new(self.x * scale, self.y * scale, self.z * scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U> Div<T> for TypedVector3D<T, U> {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn div(self, scale: T) -> Self {
|
||||
Self::new(self.x / scale, self.y / scale, self.z / scale)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Mul<T, Output=T>, U> MulAssign<T> for TypedVector3D<T, U> {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, scale: T) {
|
||||
*self = *self * scale
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy + Div<T, Output=T>, U> DivAssign<T> for TypedVector3D<T, U> {
|
||||
#[inline]
|
||||
fn div_assign(&mut self, scale: T) {
|
||||
*self = *self / scale
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float, U> TypedVector3D<T, U> {
|
||||
#[inline]
|
||||
pub fn min(self, other: Self) -> Self {
|
||||
vec3(self.x.min(other.x), self.y.min(other.y), self.z.min(other.z))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max(self, other: Self) -> Self {
|
||||
vec3(self.x.max(other.x), self.y.max(other.y), self.z.max(other.z))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Round, U> TypedVector3D<T, U> {
|
||||
/// Rounds each component to the nearest integer value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn round(&self) -> Self {
|
||||
vec3(self.x.round(), self.y.round(), self.z.round())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ceil, U> TypedVector3D<T, U> {
|
||||
/// Rounds each component to the smallest integer equal or greater than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn ceil(&self) -> Self {
|
||||
vec3(self.x.ceil(), self.y.ceil(), self.z.ceil())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Floor, U> TypedVector3D<T, U> {
|
||||
/// Rounds each component to the biggest integer equal or lower than the original value.
|
||||
///
|
||||
/// This behavior is preserved for negative values (unlike the basic cast).
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn floor(&self) -> Self {
|
||||
vec3(self.x.floor(), self.y.floor(), self.z.floor())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumCast + Copy, U> TypedVector3D<T, U> {
|
||||
/// Cast from one numeric representation to another, preserving the units.
|
||||
///
|
||||
/// When casting from floating vector to integer coordinates, the decimals are truncated
|
||||
/// as one would expect from a simple cast, but this behavior does not always make sense
|
||||
/// geometrically. Consider using round(), ceil or floor() before casting.
|
||||
#[inline]
|
||||
pub fn cast<NewT: NumCast + Copy>(&self) -> Option<TypedVector3D<NewT, U>> {
|
||||
match (NumCast::from(self.x),
|
||||
NumCast::from(self.y),
|
||||
NumCast::from(self.z)) {
|
||||
(Some(x), Some(y), Some(z)) => Some(vec3(x, y, z)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience functions for common casts
|
||||
|
||||
/// Cast into an `f32` vector.
|
||||
#[inline]
|
||||
pub fn to_f32(&self) -> TypedVector3D<f32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `usize` vector, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating vector vectors, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_usize(&self) -> TypedVector3D<usize, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `i32` vector, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating vector vectors, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i32(&self) -> TypedVector3D<i32, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
|
||||
/// Cast into an `i64` vector, truncating decimals if any.
|
||||
///
|
||||
/// When casting from floating vector vectors, it is worth considering whether
|
||||
/// to `round()`, `ceil()` or `floor()` before the cast in order to obtain
|
||||
/// the desired conversion behavior.
|
||||
#[inline]
|
||||
pub fn to_i64(&self) -> TypedVector3D<i64, U> {
|
||||
self.cast().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy+ApproxEq<T>, U> ApproxEq<TypedVector3D<T, U>> for TypedVector3D<T, U> {
|
||||
#[inline]
|
||||
fn approx_epsilon() -> Self {
|
||||
vec3(T::approx_epsilon(), T::approx_epsilon(), T::approx_epsilon())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn approx_eq(&self, other: &Self) -> bool {
|
||||
self.x.approx_eq(&other.x)
|
||||
&& self.y.approx_eq(&other.y)
|
||||
&& self.z.approx_eq(&other.z)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn approx_eq_eps(&self, other: &Self, eps: &Self) -> bool {
|
||||
self.x.approx_eq_eps(&other.x, &eps.x)
|
||||
&& self.y.approx_eq_eps(&other.y, &eps.y)
|
||||
&& self.z.approx_eq_eps(&other.z, &eps.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> Into<[T; 3]> for TypedVector3D<T, U> {
|
||||
fn into(self) -> [T; 3] {
|
||||
self.to_array()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U> From<[T; 3]> for TypedVector3D<T, U> {
|
||||
fn from(array: [T; 3]) -> Self {
|
||||
vec3(array[0], array[1], array[2])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Convenience constructor.
|
||||
#[inline]
|
||||
pub fn vec2<T: Copy, U>(x: T, y: T) -> TypedVector2D<T, U> {
|
||||
TypedVector2D::new(x, y)
|
||||
}
|
||||
|
||||
/// Convenience constructor.
|
||||
#[inline]
|
||||
pub fn vec3<T: Copy, U>(x: T, y: T, z: T) -> TypedVector3D<T, U> {
|
||||
TypedVector3D::new(x, y, z)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod vector2d {
|
||||
use super::{Vector2D, vec2};
|
||||
type Vec2 = Vector2D<f32>;
|
||||
|
||||
#[test]
|
||||
pub fn test_scalar_mul() {
|
||||
let p1: Vec2 = vec2(3.0, 5.0);
|
||||
|
||||
let result = p1 * 5.0;
|
||||
|
||||
assert_eq!(result, Vector2D::new(15.0, 25.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_dot() {
|
||||
let p1: Vec2 = vec2(2.0, 7.0);
|
||||
let p2: Vec2 = vec2(13.0, 11.0);
|
||||
assert_eq!(p1.dot(p2), 103.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_cross() {
|
||||
let p1: Vec2 = vec2(4.0, 7.0);
|
||||
let p2: Vec2 = vec2(13.0, 8.0);
|
||||
let r = p1.cross(p2);
|
||||
assert_eq!(r, -59.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_normalize() {
|
||||
let p0: Vec2 = Vec2::zero();
|
||||
let p1: Vec2 = vec2(4.0, 0.0);
|
||||
let p2: Vec2 = vec2(3.0, -4.0);
|
||||
assert_eq!(p0.normalize(), p0);
|
||||
assert_eq!(p1.normalize(), vec2(1.0, 0.0));
|
||||
assert_eq!(p2.normalize(), vec2(0.6, -0.8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_min() {
|
||||
let p1: Vec2 = vec2(1.0, 3.0);
|
||||
let p2: Vec2 = vec2(2.0, 2.0);
|
||||
|
||||
let result = p1.min(p2);
|
||||
|
||||
assert_eq!(result, vec2(1.0, 2.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_max() {
|
||||
let p1: Vec2 = vec2(1.0, 3.0);
|
||||
let p2: Vec2 = vec2(2.0, 2.0);
|
||||
|
||||
let result = p1.max(p2);
|
||||
|
||||
assert_eq!(result, vec2(2.0, 3.0));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod typedvector2d {
|
||||
use super::{TypedVector2D, vec2};
|
||||
use scale_factor::ScaleFactor;
|
||||
|
||||
pub enum Mm {}
|
||||
pub enum Cm {}
|
||||
|
||||
pub type Vector2DMm<T> = TypedVector2D<T, Mm>;
|
||||
pub type Vector2DCm<T> = TypedVector2D<T, Cm>;
|
||||
|
||||
#[test]
|
||||
pub fn test_add() {
|
||||
let p1 = Vector2DMm::new(1.0, 2.0);
|
||||
let p2 = Vector2DMm::new(3.0, 4.0);
|
||||
|
||||
let result = p1 + p2;
|
||||
|
||||
assert_eq!(result, vec2(4.0, 6.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_add_assign() {
|
||||
let mut p1 = Vector2DMm::new(1.0, 2.0);
|
||||
p1 += vec2(3.0, 4.0);
|
||||
|
||||
assert_eq!(p1, vec2(4.0, 6.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_scalar_mul() {
|
||||
let p1 = Vector2DMm::new(1.0, 2.0);
|
||||
let cm_per_mm: ScaleFactor<f32, Mm, Cm> = ScaleFactor::new(0.1);
|
||||
|
||||
let result: Vector2DCm<f32> = p1 * cm_per_mm;
|
||||
|
||||
assert_eq!(result, vec2(0.1, 0.2));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod vector3d {
|
||||
use super::{Vector3D, vec3};
|
||||
type Vec3 = Vector3D<f32>;
|
||||
|
||||
#[test]
|
||||
pub fn test_dot() {
|
||||
let p1: Vec3 = vec3(7.0, 21.0, 32.0);
|
||||
let p2: Vec3 = vec3(43.0, 5.0, 16.0);
|
||||
assert_eq!(p1.dot(p2), 918.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_cross() {
|
||||
let p1: Vec3 = vec3(4.0, 7.0, 9.0);
|
||||
let p2: Vec3 = vec3(13.0, 8.0, 3.0);
|
||||
let p3 = p1.cross(p2);
|
||||
assert_eq!(p3, vec3(-51.0, 105.0, -59.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_normalize() {
|
||||
let p0: Vec3 = Vec3::zero();
|
||||
let p1: Vec3 = vec3(0.0, -6.0, 0.0);
|
||||
let p2: Vec3 = vec3(1.0, 2.0, -2.0);
|
||||
assert_eq!(p0.normalize(), p0);
|
||||
assert_eq!(p1.normalize(), vec3(0.0, -1.0, 0.0));
|
||||
assert_eq!(p2.normalize(), vec3(1.0/3.0, 2.0/3.0, -2.0/3.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_min() {
|
||||
let p1: Vec3 = vec3(1.0, 3.0, 5.0);
|
||||
let p2: Vec3 = vec3(2.0, 2.0, -1.0);
|
||||
|
||||
let result = p1.min(p2);
|
||||
|
||||
assert_eq!(result, vec3(1.0, 2.0, -1.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_max() {
|
||||
let p1: Vec3 = vec3(1.0, 3.0, 5.0);
|
||||
let p2: Vec3 = vec3(2.0, 2.0, -1.0);
|
||||
|
||||
let result = p1.max(p2);
|
||||
|
||||
assert_eq!(result, vec3(2.0, 3.0, 5.0));
|
||||
}
|
||||
}
|
|
@ -1 +1 @@
|
|||
{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"f9b1ca6ae27d1c18215265024629a8960c31379f206d9ed20f64e0b2dcf79805",".travis.yml":"b76d49f66f842c652d40825c67791352364a6b6bbb7d8d1009f2ac79eb413e66","Cargo.toml":"42a432c2bd866b37698e064e7f72b6cf96d7aa57dcc5ae32c9474bd6eef745a2","LICENSE":"b946744aeda89b467929585fe8eeb5461847695220c1b168fb375d8abd4ea3d0","README.md":"62f99334c17b451342fcea70eb1cc27b26612616b7c1a58fab50dd493f766f32","benches/split.rs":"dfe01759652e2098f20547e0ddcc1b2937e88c6d6ddb025353c037a46b7ef85d","src/bsp.rs":"66e1690aa8540f744ee013ac0e550ecdee84633727cb3a2d8239db3597ad25d6","src/lib.rs":"21d6135c10dd820c2b9ac484cc018e1149f2bf44c315d27134edd3ecb8a7f3d2","src/naive.rs":"444d3298224009209ae329458fe8df953193b15a04da29cdd6f498572a6471bf","tests/main.rs":"d65d7fe01ff3091a9b470a2f26b28108968ca5d32a5a14defba4336df31c7d7f","tests/split.rs":"19d5bfaaf93115ddecdac0f720893c61b2ed73a0bcb4711534ac7e4500cc06ae"},"package":"da4c13e9ba1388fd628ec2bcd69f3346dec64357e9b552601b244f92189d4610"}
|
||||
{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"f9b1ca6ae27d1c18215265024629a8960c31379f206d9ed20f64e0b2dcf79805",".travis.yml":"b76d49f66f842c652d40825c67791352364a6b6bbb7d8d1009f2ac79eb413e66","Cargo.toml":"6a8c18281f4854b2f184e335d2efb7702ed920f3e66adbe84ce2013215215068","LICENSE":"b946744aeda89b467929585fe8eeb5461847695220c1b168fb375d8abd4ea3d0","README.md":"62f99334c17b451342fcea70eb1cc27b26612616b7c1a58fab50dd493f766f32","benches/split.rs":"49befe22321f34280106fdea53d93644b7757873407376247f86f9d55d09b4ab","src/bsp.rs":"1bc961e97b47f6d918384858310c60a20f9490e11404a89f379a2ad6c5705071","src/lib.rs":"c7f52a46d9ebdb9c1346b39312110aaba75821297e5f446c81a8a25706d850f5","src/naive.rs":"c7e50de094d24b609f03e3dc9599bb040a6baef84bce93ffab7af7f049fb805b","tests/main.rs":"915d915c5ca82befef82f1604cc974b072238a8d69043341589d8dd569d412d3","tests/split.rs":"a4681a788f9a9a515d4084d97ba33406a54bc0725711ade9fc955348d1703368"},"package":"f00d5b0bef85e7e218329cde2f9b75784967c62c0cc9b7faa491d81c2d35eb2a"}
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "plane-split"
|
||||
version = "0.5.0"
|
||||
version = "0.4.1"
|
||||
description = "Plane splitting"
|
||||
authors = ["Dzmitry Malyshau <kvark@mozilla.com>"]
|
||||
license = "MPL-2.0"
|
||||
|
@ -10,6 +10,6 @@ documentation = "https://docs.rs/plane-split"
|
|||
|
||||
[dependencies]
|
||||
binary-space-partition = "0.1.2"
|
||||
euclid = "0.14.2"
|
||||
euclid = "0.13"
|
||||
log = "0.3"
|
||||
num-traits = {version = "0.1.37", default-features = false}
|
||||
|
|
|
@ -5,14 +5,14 @@ extern crate plane_split;
|
|||
extern crate test;
|
||||
|
||||
use std::sync::Arc;
|
||||
use euclid::vec3;
|
||||
use euclid::TypedPoint3D;
|
||||
use plane_split::{BspSplitter, NaiveSplitter, Splitter, _make_grid};
|
||||
|
||||
#[bench]
|
||||
fn bench_naive(b: &mut test::Bencher) {
|
||||
let polys = Arc::new(_make_grid(5));
|
||||
let mut splitter = NaiveSplitter::new();
|
||||
let view = vec3(0.0, 0.0, 1.0);
|
||||
let view = TypedPoint3D::new(0.0, 0.0, 1.0);
|
||||
b.iter(|| {
|
||||
let p = polys.clone();
|
||||
splitter.solve(&p, view);
|
||||
|
@ -23,7 +23,7 @@ fn bench_naive(b: &mut test::Bencher) {
|
|||
fn bench_bsp(b: &mut test::Bencher) {
|
||||
let polys = Arc::new(_make_grid(5));
|
||||
let mut splitter = BspSplitter::new();
|
||||
let view = vec3(0.0, 0.0, 1.0);
|
||||
let view = TypedPoint3D::new(0.0, 0.0, 1.0);
|
||||
b.iter(|| {
|
||||
let p = polys.clone();
|
||||
splitter.solve(&p, view);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use binary_space_partition::{BspNode, Plane, PlaneCut};
|
||||
use euclid::{TypedPoint3D, TypedVector3D};
|
||||
use euclid::TypedPoint3D;
|
||||
use euclid::approxeq::ApproxEq;
|
||||
use num_traits::{Float, One, Zero};
|
||||
use std::{fmt, ops};
|
||||
|
@ -96,10 +96,10 @@ impl<T, U> Splitter<T, U> for BspSplitter<T, U> where
|
|||
self.tree.insert(poly);
|
||||
}
|
||||
|
||||
fn sort(&mut self, view: TypedVector3D<T, U>) -> &[Polygon<T, U>] {
|
||||
fn sort(&mut self, view: TypedPoint3D<T, U>) -> &[Polygon<T, U>] {
|
||||
//debug!("\t\ttree before sorting {:?}", self.tree);
|
||||
let poly = Polygon {
|
||||
points: [TypedPoint3D::origin(); 4],
|
||||
points: [TypedPoint3D::zero(); 4],
|
||||
normal: -view, //Note: BSP `order()` is back to front
|
||||
offset: T::zero(),
|
||||
anchor: 0,
|
||||
|
|
|
@ -20,9 +20,9 @@ mod bsp;
|
|||
mod naive;
|
||||
|
||||
use std::{fmt, mem, ops};
|
||||
use euclid::{Point2D, TypedTransform3D, TypedPoint3D, TypedVector3D, TypedRect};
|
||||
use euclid::{Point2D, TypedMatrix4D, TypedPoint3D, TypedRect};
|
||||
use euclid::approxeq::ApproxEq;
|
||||
use euclid::Trig;
|
||||
use euclid::trig::Trig;
|
||||
use num_traits::{Float, One, Zero};
|
||||
|
||||
pub use self::bsp::BspSplitter;
|
||||
|
@ -35,7 +35,7 @@ fn is_zero<T>(value: T) -> bool where
|
|||
(value * value).approx_eq(&T::zero())
|
||||
}
|
||||
|
||||
fn is_zero_vec<T, U>(vec: TypedVector3D<T, U>) -> bool where
|
||||
fn is_zero_vec<T, U>(vec: TypedPoint3D<T, U>) -> bool where
|
||||
T: Copy + Zero + ApproxEq<T> +
|
||||
ops::Add<T, Output=T> + ops::Sub<T, Output=T> + ops::Mul<T, Output=T> {
|
||||
vec.dot(vec).approx_eq(&T::zero())
|
||||
|
@ -47,7 +47,7 @@ pub struct Line<T, U> {
|
|||
/// Arbitrary point on the line.
|
||||
pub origin: TypedPoint3D<T, U>,
|
||||
/// Normalized direction of the line.
|
||||
pub dir: TypedVector3D<T, U>,
|
||||
pub dir: TypedPoint3D<T, U>,
|
||||
}
|
||||
|
||||
impl<T, U> Line<T, U> where
|
||||
|
@ -73,7 +73,7 @@ pub struct Polygon<T, U> {
|
|||
/// Points making the polygon.
|
||||
pub points: [TypedPoint3D<T, U>; 4],
|
||||
/// Normalized vector perpendicular to the polygon plane.
|
||||
pub normal: TypedVector3D<T, U>,
|
||||
pub normal: TypedPoint3D<T, U>,
|
||||
/// Constant offset from the normal plane, specified in the
|
||||
/// direction opposite to the normal.
|
||||
pub offset: T,
|
||||
|
@ -179,7 +179,7 @@ impl<T, U> Polygon<T, U> where
|
|||
{
|
||||
/// Construct a polygon from a transformed rectangle.
|
||||
pub fn from_transformed_rect<V>(rect: TypedRect<T, V>,
|
||||
transform: TypedTransform3D<T, V, U>,
|
||||
transform: TypedMatrix4D<T, V, U>,
|
||||
anchor: usize)
|
||||
-> Polygon<T, U>
|
||||
where T: Trig + ops::Neg<Output=T> {
|
||||
|
@ -196,7 +196,7 @@ impl<T, U> Polygon<T, U> where
|
|||
|
||||
let normal = (points[1] - points[0]).cross(points[2] - points[0])
|
||||
.normalize();
|
||||
let offset = -TypedVector3D::new(transform.m41, transform.m42, transform.m43).dot(normal);
|
||||
let offset = -TypedPoint3D::new(transform.m41, transform.m42, transform.m43).dot(normal);
|
||||
|
||||
Polygon {
|
||||
points: points,
|
||||
|
@ -231,7 +231,7 @@ impl<T, U> Polygon<T, U> where
|
|||
/// The distance is negative if the point is on the other side of the polygon
|
||||
/// from the direction of the normal.
|
||||
pub fn signed_distance_to(&self, point: &TypedPoint3D<T, U>) -> T {
|
||||
point.to_vector().dot(self.normal) + self.offset
|
||||
point.dot(self.normal) + self.offset
|
||||
}
|
||||
|
||||
/// Compute the distance across the line to the polygon plane,
|
||||
|
@ -283,13 +283,13 @@ impl<T, U> Polygon<T, U> where
|
|||
|
||||
/// Project this polygon onto a 3D vector, returning a line projection.
|
||||
/// Note: we can think of it as a projection to a ray placed at the origin.
|
||||
pub fn project_on(&self, vector: &TypedVector3D<T, U>) -> LineProjection<T> {
|
||||
pub fn project_on(&self, vector: &TypedPoint3D<T, U>) -> LineProjection<T> {
|
||||
LineProjection {
|
||||
markers: [
|
||||
vector.dot(self.points[0].to_vector()),
|
||||
vector.dot(self.points[1].to_vector()),
|
||||
vector.dot(self.points[2].to_vector()),
|
||||
vector.dot(self.points[3].to_vector()),
|
||||
vector.dot(self.points[0]),
|
||||
vector.dot(self.points[1]),
|
||||
vector.dot(self.points[2]),
|
||||
vector.dot(self.points[3]),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
@ -321,8 +321,7 @@ impl<T, U> Polygon<T, U> where
|
|||
// v = (d2*w - d1) / (1 - w*w) * n1 - (d2 - d1*w) / (1 - w*w) * n2
|
||||
let w = self.normal.dot(other.normal);
|
||||
let factor = T::one() / (T::one() - w * w);
|
||||
let center = TypedPoint3D::origin() +
|
||||
self.normal * ((other.offset * w - self.offset) * factor) -
|
||||
let center = self.normal * ((other.offset * w - self.offset) * factor) -
|
||||
other.normal* ((other.offset - self.offset * w) * factor);
|
||||
Intersection::Inside(Line {
|
||||
origin: center,
|
||||
|
@ -444,10 +443,10 @@ pub trait Splitter<T, U> {
|
|||
|
||||
/// Sort the produced polygon set by the ascending distance across
|
||||
/// the specified view vector. Return the sorted slice.
|
||||
fn sort(&mut self, TypedVector3D<T, U>) -> &[Polygon<T, U>];
|
||||
fn sort(&mut self, TypedPoint3D<T, U>) -> &[Polygon<T, U>];
|
||||
|
||||
/// Process a set of polygons at once.
|
||||
fn solve(&mut self, input: &[Polygon<T, U>], view: TypedVector3D<T, U>)
|
||||
fn solve(&mut self, input: &[Polygon<T, U>], view: TypedPoint3D<T, U>)
|
||||
-> &[Polygon<T, U>]
|
||||
where T: Clone, U: Clone {
|
||||
self.reset();
|
||||
|
@ -471,7 +470,7 @@ pub fn _make_grid(count: usize) -> Vec<Polygon<f32, ()>> {
|
|||
TypedPoint3D::new(len, i as f32, len),
|
||||
TypedPoint3D::new(0.0, i as f32, len),
|
||||
],
|
||||
normal: TypedVector3D::new(0.0, 1.0, 0.0),
|
||||
normal: TypedPoint3D::new(0.0, 1.0, 0.0),
|
||||
offset: -(i as f32),
|
||||
anchor: 0,
|
||||
}));
|
||||
|
@ -482,7 +481,7 @@ pub fn _make_grid(count: usize) -> Vec<Polygon<f32, ()>> {
|
|||
TypedPoint3D::new(i as f32, len, len),
|
||||
TypedPoint3D::new(i as f32, 0.0, len),
|
||||
],
|
||||
normal: TypedVector3D::new(1.0, 0.0, 0.0),
|
||||
normal: TypedPoint3D::new(1.0, 0.0, 0.0),
|
||||
offset: -(i as f32),
|
||||
anchor: 0,
|
||||
}));
|
||||
|
@ -493,7 +492,7 @@ pub fn _make_grid(count: usize) -> Vec<Polygon<f32, ()>> {
|
|||
TypedPoint3D::new(len, len, i as f32),
|
||||
TypedPoint3D::new(0.0, len, i as f32),
|
||||
],
|
||||
normal: TypedVector3D::new(0.0, 0.0, 1.0),
|
||||
normal: TypedPoint3D::new(0.0, 0.0, 1.0),
|
||||
offset: -(i as f32),
|
||||
anchor: 0,
|
||||
}));
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::{fmt, ops};
|
||||
use std::cmp::Ordering;
|
||||
use {Intersection, Line, Polygon, Splitter};
|
||||
use euclid::TypedVector3D;
|
||||
use euclid::TypedPoint3D;
|
||||
use euclid::approxeq::ApproxEq;
|
||||
use num_traits::{Float, One, Zero};
|
||||
|
||||
|
@ -27,8 +27,8 @@ impl<T, U> NaiveSplitter<T, U> {
|
|||
/// Find a closest intersection point between two polygons,
|
||||
/// across the specified direction.
|
||||
fn intersect_across<T, U>(a: &Polygon<T, U>, b: &Polygon<T, U>,
|
||||
dir: TypedVector3D<T, U>)
|
||||
-> TypedVector3D<T, U>
|
||||
dir: TypedPoint3D<T, U>)
|
||||
-> TypedPoint3D<T, U>
|
||||
where
|
||||
T: Copy + fmt::Debug + PartialOrd + ApproxEq<T> +
|
||||
ops::Sub<T, Output=T> + ops::Add<T, Output=T> +
|
||||
|
@ -136,11 +136,11 @@ impl<
|
|||
}
|
||||
|
||||
//TODO: verify/prove that the sorting approach is consistent
|
||||
fn sort(&mut self, view: TypedVector3D<T, U>) -> &[Polygon<T, U>] {
|
||||
fn sort(&mut self, view: TypedPoint3D<T, U>) -> &[Polygon<T, U>] {
|
||||
// choose the most perpendicular axis among these two
|
||||
let axis_pre = {
|
||||
let axis_pre0 = TypedVector3D::new(T::one(), T::zero(), T::zero());
|
||||
let axis_pre1 = TypedVector3D::new(T::zero(), T::one(), T::zero());
|
||||
let axis_pre0 = TypedPoint3D::new(T::one(), T::zero(), T::zero());
|
||||
let axis_pre1 = TypedPoint3D::new(T::zero(), T::one(), T::zero());
|
||||
if view.dot(axis_pre0).abs() < view.dot(axis_pre1).abs() {
|
||||
axis_pre0
|
||||
} else {
|
||||
|
@ -160,7 +160,7 @@ impl<
|
|||
let comp_y = intersect_across(a, b, axis_y);
|
||||
// line that tries to intersect both
|
||||
let line = Line {
|
||||
origin: (comp_x + comp_y).to_point(),
|
||||
origin: comp_x + comp_y,
|
||||
dir: view,
|
||||
};
|
||||
debug!("\t\tGot {:?}", line);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
extern crate euclid;
|
||||
extern crate plane_split;
|
||||
|
||||
use euclid::{Radians, TypedRect, TypedSize2D, TypedTransform3D, point2, point3, vec3};
|
||||
use euclid::{Point2D, Radians, TypedPoint2D, TypedPoint3D, TypedRect, TypedSize2D, TypedMatrix4D};
|
||||
use euclid::approxeq::ApproxEq;
|
||||
use plane_split::{Intersection, Line, LineProjection, Polygon};
|
||||
|
||||
|
@ -16,36 +16,36 @@ fn line_proj_bounds() {
|
|||
fn valid() {
|
||||
let poly_a: Polygon<f32, ()> = Polygon {
|
||||
points: [
|
||||
point3(0.0, 0.0, 0.0),
|
||||
point3(1.0, 1.0, 1.0),
|
||||
point3(1.0, 1.0, 0.0),
|
||||
point3(0.0, 1.0, 1.0),
|
||||
TypedPoint3D::new(0.0, 0.0, 0.0),
|
||||
TypedPoint3D::new(1.0, 1.0, 1.0),
|
||||
TypedPoint3D::new(1.0, 1.0, 0.0),
|
||||
TypedPoint3D::new(0.0, 1.0, 1.0),
|
||||
],
|
||||
normal: vec3(0.0, 1.0, 0.0),
|
||||
normal: TypedPoint3D::new(0.0, 1.0, 0.0),
|
||||
offset: -1.0,
|
||||
anchor: 0,
|
||||
};
|
||||
assert!(!poly_a.is_valid()); // points[0] is outside
|
||||
let poly_b: Polygon<f32, ()> = Polygon {
|
||||
points: [
|
||||
point3(0.0, 1.0, 0.0),
|
||||
point3(1.0, 1.0, 1.0),
|
||||
point3(1.0, 1.0, 0.0),
|
||||
point3(0.0, 1.0, 1.0),
|
||||
TypedPoint3D::new(0.0, 1.0, 0.0),
|
||||
TypedPoint3D::new(1.0, 1.0, 1.0),
|
||||
TypedPoint3D::new(1.0, 1.0, 0.0),
|
||||
TypedPoint3D::new(0.0, 1.0, 1.0),
|
||||
],
|
||||
normal: vec3(0.0, 1.0, 0.0),
|
||||
normal: TypedPoint3D::new(0.0, 1.0, 0.0),
|
||||
offset: -1.0,
|
||||
anchor: 0,
|
||||
};
|
||||
assert!(!poly_b.is_valid()); // winding is incorrect
|
||||
let poly_c: Polygon<f32, ()> = Polygon {
|
||||
points: [
|
||||
point3(0.0, 0.0, 1.0),
|
||||
point3(1.0, 0.0, 1.0),
|
||||
point3(1.0, 1.0, 1.0),
|
||||
point3(0.0, 1.0, 1.0),
|
||||
TypedPoint3D::new(0.0, 0.0, 1.0),
|
||||
TypedPoint3D::new(1.0, 0.0, 1.0),
|
||||
TypedPoint3D::new(1.0, 1.0, 1.0),
|
||||
TypedPoint3D::new(0.0, 1.0, 1.0),
|
||||
],
|
||||
normal: vec3(0.0, 0.0, 1.0),
|
||||
normal: TypedPoint3D::new(0.0, 0.0, 1.0),
|
||||
offset: -1.0,
|
||||
anchor: 0,
|
||||
};
|
||||
|
@ -54,10 +54,10 @@ fn valid() {
|
|||
|
||||
#[test]
|
||||
fn from_transformed_rect() {
|
||||
let rect: TypedRect<f32, ()> = TypedRect::new(point2(10.0, 10.0), TypedSize2D::new(20.0, 30.0));
|
||||
let transform: TypedTransform3D<f32, (), ()> =
|
||||
TypedTransform3D::create_rotation(0.5f32.sqrt(), 0.0, 0.5f32.sqrt(), Radians::new(5.0))
|
||||
.pre_translate(vec3(0.0, 0.0, 10.0));
|
||||
let rect: TypedRect<f32, ()> = TypedRect::new(TypedPoint2D::new(10.0, 10.0), TypedSize2D::new(20.0, 30.0));
|
||||
let transform: TypedMatrix4D<f32, (), ()> =
|
||||
TypedMatrix4D::create_rotation(0.5f32.sqrt(), 0.0, 0.5f32.sqrt(), Radians::new(5.0))
|
||||
.pre_translated(0.0, 0.0, 10.0);
|
||||
let poly = Polygon::from_transformed_rect(rect, transform, 0);
|
||||
assert!(poly.is_valid());
|
||||
}
|
||||
|
@ -66,45 +66,45 @@ fn from_transformed_rect() {
|
|||
fn untransform_point() {
|
||||
let poly: Polygon<f32, ()> = Polygon {
|
||||
points: [
|
||||
point3(0.0, 0.0, 0.0),
|
||||
point3(0.5, 1.0, 0.0),
|
||||
point3(1.5, 1.0, 0.0),
|
||||
point3(1.0, 0.0, 0.0),
|
||||
TypedPoint3D::new(0.0, 0.0, 0.0),
|
||||
TypedPoint3D::new(0.5, 1.0, 0.0),
|
||||
TypedPoint3D::new(1.5, 1.0, 0.0),
|
||||
TypedPoint3D::new(1.0, 0.0, 0.0),
|
||||
],
|
||||
normal: vec3(0.0, 1.0, 0.0),
|
||||
normal: TypedPoint3D::new(0.0, 1.0, 0.0),
|
||||
offset: 0.0,
|
||||
anchor: 0,
|
||||
};
|
||||
assert_eq!(poly.untransform_point(poly.points[0]), point2(0.0, 0.0));
|
||||
assert_eq!(poly.untransform_point(poly.points[1]), point2(1.0, 0.0));
|
||||
assert_eq!(poly.untransform_point(poly.points[2]), point2(1.0, 1.0));
|
||||
assert_eq!(poly.untransform_point(poly.points[3]), point2(0.0, 1.0));
|
||||
assert_eq!(poly.untransform_point(poly.points[0]), Point2D::new(0.0, 0.0));
|
||||
assert_eq!(poly.untransform_point(poly.points[1]), Point2D::new(1.0, 0.0));
|
||||
assert_eq!(poly.untransform_point(poly.points[2]), Point2D::new(1.0, 1.0));
|
||||
assert_eq!(poly.untransform_point(poly.points[3]), Point2D::new(0.0, 1.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn are_outside() {
|
||||
let poly: Polygon<f32, ()> = Polygon {
|
||||
points: [
|
||||
point3(0.0, 0.0, 1.0),
|
||||
point3(1.0, 0.0, 1.0),
|
||||
point3(1.0, 1.0, 1.0),
|
||||
point3(0.0, 1.0, 1.0),
|
||||
TypedPoint3D::new(0.0, 0.0, 1.0),
|
||||
TypedPoint3D::new(1.0, 0.0, 1.0),
|
||||
TypedPoint3D::new(1.0, 1.0, 1.0),
|
||||
TypedPoint3D::new(0.0, 1.0, 1.0),
|
||||
],
|
||||
normal: vec3(0.0, 0.0, 1.0),
|
||||
normal: TypedPoint3D::new(0.0, 0.0, 1.0),
|
||||
offset: -1.0,
|
||||
anchor: 0,
|
||||
};
|
||||
assert!(poly.is_valid());
|
||||
assert!(poly.are_outside(&[
|
||||
point3(0.0, 0.0, 1.1),
|
||||
point3(1.0, 1.0, 2.0),
|
||||
TypedPoint3D::new(0.0, 0.0, 1.1),
|
||||
TypedPoint3D::new(1.0, 1.0, 2.0),
|
||||
]));
|
||||
assert!(poly.are_outside(&[
|
||||
point3(0.5, 0.5, 1.0),
|
||||
TypedPoint3D::new(0.5, 0.5, 1.0),
|
||||
]));
|
||||
assert!(!poly.are_outside(&[
|
||||
point3(0.0, 0.0, 1.0),
|
||||
point3(0.0, 0.0, -1.0),
|
||||
TypedPoint3D::new(0.0, 0.0, 1.0),
|
||||
TypedPoint3D::new(0.0, 0.0, -1.0),
|
||||
]));
|
||||
}
|
||||
|
||||
|
@ -112,24 +112,24 @@ fn are_outside() {
|
|||
fn intersect() {
|
||||
let poly_a: Polygon<f32, ()> = Polygon {
|
||||
points: [
|
||||
point3(0.0, 0.0, 1.0),
|
||||
point3(1.0, 0.0, 1.0),
|
||||
point3(1.0, 1.0, 1.0),
|
||||
point3(0.0, 1.0, 1.0),
|
||||
TypedPoint3D::new(0.0, 0.0, 1.0),
|
||||
TypedPoint3D::new(1.0, 0.0, 1.0),
|
||||
TypedPoint3D::new(1.0, 1.0, 1.0),
|
||||
TypedPoint3D::new(0.0, 1.0, 1.0),
|
||||
],
|
||||
normal: vec3(0.0, 0.0, 1.0),
|
||||
normal: TypedPoint3D::new(0.0, 0.0, 1.0),
|
||||
offset: -1.0,
|
||||
anchor: 0,
|
||||
};
|
||||
assert!(poly_a.is_valid());
|
||||
let poly_b: Polygon<f32, ()> = Polygon {
|
||||
points: [
|
||||
point3(0.5, 0.0, 2.0),
|
||||
point3(0.5, 1.0, 2.0),
|
||||
point3(0.5, 1.0, 0.0),
|
||||
point3(0.5, 0.0, 0.0),
|
||||
TypedPoint3D::new(0.5, 0.0, 2.0),
|
||||
TypedPoint3D::new(0.5, 1.0, 2.0),
|
||||
TypedPoint3D::new(0.5, 1.0, 0.0),
|
||||
TypedPoint3D::new(0.5, 0.0, 0.0),
|
||||
],
|
||||
normal: vec3(1.0, 0.0, 0.0),
|
||||
normal: TypedPoint3D::new(1.0, 0.0, 0.0),
|
||||
offset: -0.5,
|
||||
anchor: 0,
|
||||
};
|
||||
|
@ -149,24 +149,24 @@ fn intersect() {
|
|||
|
||||
let poly_c: Polygon<f32, ()> = Polygon {
|
||||
points: [
|
||||
point3(0.0, -1.0, 2.0),
|
||||
point3(0.0, -1.0, 0.0),
|
||||
point3(0.0, 0.0, 0.0),
|
||||
point3(0.0, 0.0, 2.0),
|
||||
TypedPoint3D::new(0.0, -1.0, 2.0),
|
||||
TypedPoint3D::new(0.0, -1.0, 0.0),
|
||||
TypedPoint3D::new(0.0, 0.0, 0.0),
|
||||
TypedPoint3D::new(0.0, 0.0, 2.0),
|
||||
],
|
||||
normal: vec3(1.0, 0.0, 0.0),
|
||||
normal: TypedPoint3D::new(1.0, 0.0, 0.0),
|
||||
offset: 0.0,
|
||||
anchor: 0,
|
||||
};
|
||||
assert!(poly_c.is_valid());
|
||||
let poly_d: Polygon<f32, ()> = Polygon {
|
||||
points: [
|
||||
point3(0.0, 0.0, 0.5),
|
||||
point3(1.0, 0.0, 0.5),
|
||||
point3(1.0, 1.0, 0.5),
|
||||
point3(0.0, 1.0, 0.5),
|
||||
TypedPoint3D::new(0.0, 0.0, 0.5),
|
||||
TypedPoint3D::new(1.0, 0.0, 0.5),
|
||||
TypedPoint3D::new(1.0, 1.0, 0.5),
|
||||
TypedPoint3D::new(0.0, 1.0, 0.5),
|
||||
],
|
||||
normal: vec3(0.0, 0.0, 1.0),
|
||||
normal: TypedPoint3D::new(0.0, 0.0, 1.0),
|
||||
offset: -0.5,
|
||||
anchor: 0,
|
||||
};
|
||||
|
@ -195,43 +195,43 @@ fn test_cut(poly_base: &Polygon<f32, ()>, extra_count: u8, line: Line<f32, ()>)
|
|||
fn split() {
|
||||
let poly: Polygon<f32, ()> = Polygon {
|
||||
points: [
|
||||
point3(0.0, 1.0, 0.0),
|
||||
point3(1.0, 1.0, 0.0),
|
||||
point3(1.0, 1.0, 1.0),
|
||||
point3(0.0, 1.0, 1.0),
|
||||
TypedPoint3D::new(0.0, 1.0, 0.0),
|
||||
TypedPoint3D::new(1.0, 1.0, 0.0),
|
||||
TypedPoint3D::new(1.0, 1.0, 1.0),
|
||||
TypedPoint3D::new(0.0, 1.0, 1.0),
|
||||
],
|
||||
normal: vec3(0.0, 1.0, 0.0),
|
||||
normal: TypedPoint3D::new(0.0, 1.0, 0.0),
|
||||
offset: -1.0,
|
||||
anchor: 0,
|
||||
};
|
||||
|
||||
// non-intersecting line
|
||||
test_cut(&poly, 0, Line {
|
||||
origin: point3(0.0, 1.0, 0.5),
|
||||
dir: vec3(0.0, 1.0, 0.0),
|
||||
origin: TypedPoint3D::new(0.0, 1.0, 0.5),
|
||||
dir: TypedPoint3D::new(0.0, 1.0, 0.0),
|
||||
});
|
||||
|
||||
// simple cut (diff=2)
|
||||
test_cut(&poly, 1, Line {
|
||||
origin: point3(0.0, 1.0, 0.5),
|
||||
dir: vec3(1.0, 0.0, 0.0),
|
||||
origin: TypedPoint3D::new(0.0, 1.0, 0.5),
|
||||
dir: TypedPoint3D::new(1.0, 0.0, 0.0),
|
||||
});
|
||||
|
||||
// complex cut (diff=1, wrapped)
|
||||
test_cut(&poly, 2, Line {
|
||||
origin: point3(0.0, 1.0, 0.5),
|
||||
dir: vec3(0.5f32.sqrt(), 0.0, -0.5f32.sqrt()),
|
||||
origin: TypedPoint3D::new(0.0, 1.0, 0.5),
|
||||
dir: TypedPoint3D::new(0.5f32.sqrt(), 0.0, -0.5f32.sqrt()),
|
||||
});
|
||||
|
||||
// complex cut (diff=1, non-wrapped)
|
||||
test_cut(&poly, 2, Line {
|
||||
origin: point3(0.5, 1.0, 0.0),
|
||||
dir: vec3(0.5f32.sqrt(), 0.0, 0.5f32.sqrt()),
|
||||
origin: TypedPoint3D::new(0.5, 1.0, 0.0),
|
||||
dir: TypedPoint3D::new(0.5f32.sqrt(), 0.0, 0.5f32.sqrt()),
|
||||
});
|
||||
|
||||
// complex cut (diff=3)
|
||||
test_cut(&poly, 2, Line {
|
||||
origin: point3(0.5, 1.0, 0.0),
|
||||
dir: vec3(-0.5f32.sqrt(), 0.0, 0.5f32.sqrt()),
|
||||
origin: TypedPoint3D::new(0.5, 1.0, 0.0),
|
||||
dir: TypedPoint3D::new(-0.5f32.sqrt(), 0.0, 0.5f32.sqrt()),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,13 +2,13 @@ extern crate euclid;
|
|||
extern crate plane_split;
|
||||
|
||||
use std::f32::consts::FRAC_PI_4;
|
||||
use euclid::{Radians, TypedTransform3D, TypedRect, vec3};
|
||||
use euclid::{Radians, TypedMatrix4D, TypedPoint2D, TypedPoint3D, TypedSize2D, TypedRect};
|
||||
use plane_split::{BspSplitter, NaiveSplitter, Polygon, Splitter, _make_grid};
|
||||
|
||||
|
||||
fn grid_impl(count: usize, splitter: &mut Splitter<f32, ()>) {
|
||||
let polys = _make_grid(count);
|
||||
let result = splitter.solve(&polys, vec3(0.0, 0.0, 1.0));
|
||||
let result = splitter.solve(&polys, TypedPoint3D::new(0.0, 0.0, 1.0));
|
||||
assert_eq!(result.len(), count + count*count + count*count*count);
|
||||
}
|
||||
|
||||
|
@ -24,21 +24,21 @@ fn grid_bsp() {
|
|||
|
||||
|
||||
fn sort_rotation(splitter: &mut Splitter<f32, ()>) {
|
||||
let transform0: TypedTransform3D<f32, (), ()> =
|
||||
TypedTransform3D::create_rotation(0.0, 1.0, 0.0, Radians::new(-FRAC_PI_4));
|
||||
let transform1: TypedTransform3D<f32, (), ()> =
|
||||
TypedTransform3D::create_rotation(0.0, 1.0, 0.0, Radians::new(0.0));
|
||||
let transform2: TypedTransform3D<f32, (), ()> =
|
||||
TypedTransform3D::create_rotation(0.0, 1.0, 0.0, Radians::new(FRAC_PI_4));
|
||||
let transform0: TypedMatrix4D<f32, (), ()> =
|
||||
TypedMatrix4D::create_rotation(0.0, 1.0, 0.0, Radians::new(-FRAC_PI_4));
|
||||
let transform1: TypedMatrix4D<f32, (), ()> =
|
||||
TypedMatrix4D::create_rotation(0.0, 1.0, 0.0, Radians::new(0.0));
|
||||
let transform2: TypedMatrix4D<f32, (), ()> =
|
||||
TypedMatrix4D::create_rotation(0.0, 1.0, 0.0, Radians::new(FRAC_PI_4));
|
||||
|
||||
let rect: TypedRect<f32, ()> = euclid::rect(-10.0, -10.0, 20.0, 20.0);
|
||||
let rect: TypedRect<f32, ()> = TypedRect::new(TypedPoint2D::new(-10.0, -10.0), TypedSize2D::new(20.0, 20.0));
|
||||
let polys = [
|
||||
Polygon::from_transformed_rect(rect, transform0, 0),
|
||||
Polygon::from_transformed_rect(rect, transform1, 1),
|
||||
Polygon::from_transformed_rect(rect, transform2, 2),
|
||||
];
|
||||
|
||||
let result = splitter.solve(&polys, vec3(0.0, 0.0, -1.0));
|
||||
let result = splitter.solve(&polys, TypedPoint3D::new(0.0, 0.0, -1.0));
|
||||
let ids: Vec<_> = result.iter().map(|poly| poly.anchor).collect();
|
||||
assert_eq!(&ids, &[2, 1, 0, 1, 2]);
|
||||
}
|
||||
|
@ -56,13 +56,13 @@ fn rotation_bsp() {
|
|||
|
||||
fn sort_trivial(splitter: &mut Splitter<f32, ()>) {
|
||||
let anchors: Vec<_> = (0usize .. 10).collect();
|
||||
let rect: TypedRect<f32, ()> = euclid::rect(-10.0, -10.0, 20.0, 20.0);
|
||||
let rect: TypedRect<f32, ()> = TypedRect::new(TypedPoint2D::new(-10.0, -10.0), TypedSize2D::new(20.0, 20.0));
|
||||
let polys: Vec<_> = anchors.iter().map(|&anchor| {
|
||||
let transform: TypedTransform3D<f32, (), ()> = TypedTransform3D::create_translation(0.0, 0.0, anchor as f32);
|
||||
let transform: TypedMatrix4D<f32, (), ()> = TypedMatrix4D::create_translation(0.0, 0.0, anchor as f32);
|
||||
Polygon::from_transformed_rect(rect, transform, anchor)
|
||||
}).collect();
|
||||
|
||||
let result = splitter.solve(&polys, vec3(0.0, 0.0, -1.0));
|
||||
let result = splitter.solve(&polys, TypedPoint3D::new(0.0, 0.0, -1.0));
|
||||
let anchors1: Vec<_> = result.iter().map(|p| p.anchor).collect();
|
||||
let mut anchors2 = anchors1.clone();
|
||||
anchors2.sort_by_key(|&a| -(a as i32));
|
||||
|
|
|
@ -325,17 +325,6 @@ dependencies = [
|
|||
"serde 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "euclid"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.5"
|
||||
|
@ -679,11 +668,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "plane-split"
|
||||
version = "0.5.0"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"binary-space-partition 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -1160,7 +1149,7 @@ dependencies = [
|
|||
"core-graphics 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-text 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dwrote 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"freetype 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gamma-lut 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1168,7 +1157,7 @@ dependencies = [
|
|||
"lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plane-split 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plane-split 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1180,7 +1169,7 @@ name = "webrender_bindings"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"app_units 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gleam 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1198,7 +1187,7 @@ dependencies = [
|
|||
"core-foundation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-graphics 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dwrote 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gleam 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"heapsize 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1258,7 +1247,6 @@ dependencies = [
|
|||
"checksum encoding_rs 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e00a1b1e95eb46988805ceee6f34cd95c46a6753e290cb3ff0486931989d4a4c"
|
||||
"checksum env_logger 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ed39959122ea027670b704fb70539f4286ddf4a49eefede23bf0b4b2a069ec03"
|
||||
"checksum euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6083f113c422ff9cd855a1cf6cc8ec0903606c0eb43a0c6a0ced3bdc9731e4c1"
|
||||
"checksum euclid 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995b21c36b37e0f18ed9ba1714378a337e3ff19a6e5e952ea94b0f3dd4e12fbc"
|
||||
"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
|
||||
"checksum freetype 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fde23272c687e4570aefec06cb71174ec0f5284b725deac4e77ba2665d635faf"
|
||||
"checksum futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "55f0008e13fc853f79ea8fc86e931486860d4c4c156cdffb59fa5f7fa833660a"
|
||||
|
@ -1296,7 +1284,7 @@ dependencies = [
|
|||
"checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03"
|
||||
"checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2"
|
||||
"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
|
||||
"checksum plane-split 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "da4c13e9ba1388fd628ec2bcd69f3346dec64357e9b552601b244f92189d4610"
|
||||
"checksum plane-split 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f00d5b0bef85e7e218329cde2f9b75784967c62c0cc9b7faa491d81c2d35eb2a"
|
||||
"checksum precomputed-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf1fc3616b3ef726a847f2cd2388c646ef6a1f1ba4835c2629004da48184150"
|
||||
"checksum procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f566249236c6ca4340f7ca78968271f0ed2b0f234007a61b66f9ecd0af09260"
|
||||
"checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3"
|
||||
|
|
|
@ -323,17 +323,6 @@ dependencies = [
|
|||
"serde 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "euclid"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.5"
|
||||
|
@ -666,11 +655,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "plane-split"
|
||||
version = "0.5.0"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"binary-space-partition 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -1147,7 +1136,7 @@ dependencies = [
|
|||
"core-graphics 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-text 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dwrote 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"freetype 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gamma-lut 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1155,7 +1144,7 @@ dependencies = [
|
|||
"lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plane-split 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plane-split 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1167,7 +1156,7 @@ name = "webrender_bindings"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"app_units 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gleam 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1185,7 +1174,7 @@ dependencies = [
|
|||
"core-foundation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-graphics 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dwrote 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gleam 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"heapsize 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1245,7 +1234,6 @@ dependencies = [
|
|||
"checksum encoding_rs 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e00a1b1e95eb46988805ceee6f34cd95c46a6753e290cb3ff0486931989d4a4c"
|
||||
"checksum env_logger 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ed39959122ea027670b704fb70539f4286ddf4a49eefede23bf0b4b2a069ec03"
|
||||
"checksum euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6083f113c422ff9cd855a1cf6cc8ec0903606c0eb43a0c6a0ced3bdc9731e4c1"
|
||||
"checksum euclid 0.14.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995b21c36b37e0f18ed9ba1714378a337e3ff19a6e5e952ea94b0f3dd4e12fbc"
|
||||
"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
|
||||
"checksum freetype 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fde23272c687e4570aefec06cb71174ec0f5284b725deac4e77ba2665d635faf"
|
||||
"checksum futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "55f0008e13fc853f79ea8fc86e931486860d4c4c156cdffb59fa5f7fa833660a"
|
||||
|
@ -1283,7 +1271,7 @@ dependencies = [
|
|||
"checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03"
|
||||
"checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2"
|
||||
"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
|
||||
"checksum plane-split 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "da4c13e9ba1388fd628ec2bcd69f3346dec64357e9b552601b244f92189d4610"
|
||||
"checksum plane-split 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f00d5b0bef85e7e218329cde2f9b75784967c62c0cc9b7faa491d81c2d35eb2a"
|
||||
"checksum precomputed-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf1fc3616b3ef726a847f2cd2388c646ef6a1f1ba4835c2629004da48184150"
|
||||
"checksum procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f566249236c6ca4340f7ca78968271f0ed2b0f234007a61b66f9ecd0af09260"
|
||||
"checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3"
|
||||
|
|
Загрузка…
Ссылка в новой задаче