<commit_before>// Copyright 2016 Matthew D. Michelotti
//
// 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.

mod solvers;
mod grid;
mod collider;
mod events;

pub use self::collider::*;

use geom::*;
use geom_ext::*;
use std::f64;

const HIGH_TIME: f64 = 1e50;

/// Type used as a handle for referencing hitboxes in a `Collider` instance.
pub type HitboxId = u64;

#[derive(PartialEq, Clone, Debug)]
pub struct Hitbox {
    pub shape: PlacedShape,
    pub vel: PlacedShape,
    pub duration: f64
}

impl Hitbox {
    pub fn new(shape: PlacedShape) -> Hitbox {
        Hitbox {
            shape : shape,
            vel : PlacedShape::new(Vec2::zero(), Shape::new(shape.kind(), 0.0, 0.0)),
            duration : f64::INFINITY
        }
    }

    fn advance(&mut self, orig_time: f64, new_time: f64) {
        assert!(orig_time <= new_time, "requires orig_time <= new_time");
        let delta = new_time - orig_time;
        if delta != 0.0 {
            self.shape = self.advanced_shape(delta);
            let end_time = orig_time + self.duration;
            assert!(new_time <= end_time, "tried to advance Hitbox beyond its duration");
            self.duration = end_time - new_time;
        }
    }

    fn advanced_shape(&self, time: f64) -> PlacedShape {
        assert!(time <= HIGH_TIME, "requires time <= {}", HIGH_TIME);
        self.shape + self.vel*time
    }

    fn bounding_box(&self) -> PlacedShape {
        self.bounding_box_for(self.duration)
    }

    fn bounding_box_for(&self, duration: f64) -> PlacedShape {
        if self.vel.is_zero() {
            self.shape.as_rect()
        } else {
            let end_shape = self.advanced_shape(duration);
            self.shape.bounding_box(&end_shape)
        }
    }

    fn collide_time(&self, other: &Hitbox) -> f64 {
        solvers::collide_time(self, other)
    }

    fn separate_time(&self, other: &Hitbox, padding: f64) -> f64 {
        solvers::separate_time(self, other, padding)
    }

    fn validate(&self, min_size: f64) {
        assert!(!self.duration.is_nan() && self.duration >= 0.0, "duration must be non-negative");
        assert!(self.shape.kind() == self.vel.kind(), "shape and vel have different kinds");
        assert!(self.shape.width() >= min_size && self.shape.height() >= min_size, "shape width/height must be at least {}", min_size);
    }

    fn time_until_too_small(&self, min_size: f64) -> f64 {
        let min_size = min_size*0.9;
        assert!(self.shape.width() > min_size && self.shape.height() > min_size, "illegal state");
        let mut time = f64::INFINITY;
        if self.vel.width() < 0.0 { time = time.min((min_size - self.shape.width())/self.vel.width()); }
        if self.vel.height() < 0.0 { time = time.min((min_size - self.shape.height())/self.vel.height()); }
        time
    }
}

pub mod inter {
    pub type Group = u32;

    static DEFAULT_GROUPS: [Group; 1] = [0];

    pub trait Interactivity {
        fn group(&self) -> Option<Group> { Some(0) }
        fn interact_groups(&self) -> &'static [Group] { &DEFAULT_GROUPS }
        fn can_interact(&self, other: &Self) -> bool;
    }

    #[derive(Default)]
    pub struct DefaultInteractivity;

    impl Interactivity for DefaultInteractivity {
        fn can_interact(&self, _other: &Self) -> bool { true }
    }
}

#[cfg(test)]
mod tests {
    use geom::*;
    use core::*;
    use std::f64;

    #[test]
    fn test_rect_rect_collision() {
        let mut a = Hitbox::new(PlacedShape::new(Vec2::new(-11.0, 0.0), Shape::new_rect(2.0, 2.0)));
        a.vel.pos = Vec2::new(2.0, 0.0);
        a.duration = 100.0;
        let mut b = Hitbox::new(PlacedShape::new(Vec2::new(12.0, 2.0), Shape::new_rect(2.0, 4.0)));
        b.vel.pos = Vec2::new(-0.5, 0.0);
        b.vel.shape = Shape::new_rect(1.0, 0.0);
        b.duration = 100.0;
        assert!(a.collide_time(&b) == 7.0);
        assert!(b.collide_time(&a) == 7.0);
        assert!(a.separate_time(&b, 0.1) == 0.0);
    }

    #[test]
    fn test_circle_circle_collision() {
        let sqrt2 = (2.0f64).sqrt();
        let mut a = Hitbox::new(PlacedShape::new(Vec2::new(-0.1*sqrt2, 0.0), Shape::new_circle(2.0)));
        a.vel.pos = Vec2::new(0.1, 0.0);
        a.duration = 100.0;
        let mut b = Hitbox::new(PlacedShape::new(Vec2::new(3.0*sqrt2, 0.0), Shape::new_circle(2.0 + sqrt2*0.1)));
        b.vel.pos = Vec2::new(-2.0, 1.0);
        b.vel.shape = Shape::new_circle(-0.1);
        b.duration = 100.0;
        assert!((a.collide_time(&b) - sqrt2).abs() < 1e-7);
        assert!(a.separate_time(&b, 0.1) == 0.0);
    }

    #[test]
    fn test_rect_circle_collision() {
        let mut a = Hitbox::new(PlacedShape::new(Vec2::new(-11.0, 0.0), Shape::new_circle(2.0)));
        a.vel.pos = Vec2::new(2.0, 0.0);
        a.duration = 100.0;
        let mut b = Hitbox::new(PlacedShape::new(Vec2::new(12.0, 2.0), Shape::new_rect(2.0, 4.0)));
        b.vel.pos = Vec2::new(-1.0, 0.0);
        b.duration = 100.0;
        assert!(a.collide_time(&b) == 7.0);
        assert!(b.collide_time(&a) == 7.0);
        assert!(a.separate_time(&b, 0.1) == 0.0);
    }

    #[test]
    fn test_rect_rect_separation() {
        let mut a = Hitbox::new(PlacedShape::new(Vec2::new(0.0, 0.0), Shape::new_rect(6.0, 4.0)));
        a.vel.pos = Vec2::new(1.0, 1.0);
        a.duration = 100.0;
        let mut b = Hitbox::new(PlacedShape::new(Vec2::new(1.0, 0.0), Shape::new_rect(4.0, 4.0)));
        b.vel.pos = Vec2::new(0.5, 0.0);
        b.duration = 100.0;
        assert!(a.separate_time(&b, 0.1) == 4.1);
        assert!(b.separate_time(&a, 0.1) == 4.1);
        assert!(a.collide_time(&b) == 0.0);
    }

    #[test]
    fn test_circle_circle_separation() {
        let sqrt2 = (2.0f64).sqrt();
        let mut a = Hitbox::new(PlacedShape::new(Vec2::new(2.0, 5.0), Shape::new_circle(2.0)));
        a.duration = 100.0;
        let mut b = Hitbox::new(PlacedShape::new(Vec2::new(3.0, 4.0), Shape::new_circle(1.8)));
        b.vel.pos = Vec2::new(-1.0, 1.0);
        b.duration = 100.0;
        assert!(a.separate_time(&b, 0.1) == 1.0 + sqrt2);
        assert!(b.separate_time(&a, 0.1) == 1.0 + sqrt2);
        assert!(a.collide_time(&b) == 0.0);
    }

    #[test]
    fn test_rect_circle_separation() {
        let sqrt2 = (2.0f64).sqrt();
        let mut a = Hitbox::new(PlacedShape::new(Vec2::new(4.0, 2.0), Shape::new_rect(4.0, 6.0)));
        a.duration = 100.0;
        let mut b = Hitbox::new(PlacedShape::new(Vec2::new(3.0, 4.0), Shape::new_circle(3.8)));
        b.vel.pos = Vec2::new(-1.0, 1.0);
        b.duration = 100.0;
        assert!(a.separate_time(&b, 0.1) == 1.0 + sqrt2);
        assert!(b.separate_time(&a, 0.1) == 1.0 + sqrt2);
        assert!(a.collide_time(&b) == 0.0);
    }

    #[test]
    fn test_no_collision() {
        let mut a = Hitbox::new(PlacedShape::new(Vec2::new(-11.0, 0.0), Shape::new_rect(2.0, 2.0)));
        a.vel.pos = Vec2::new(2.0, 0.0);
        a.duration = 100.0;
        let mut b = Hitbox::new(PlacedShape::new(Vec2::new(12.0, 2.0), Shape::new_rect(2.0, 4.0)));
        b.vel.pos = Vec2::new(-1.0, 1.0);
        b.duration = 100.0;
        assert!(a.collide_time(&b) == f64::INFINITY);
        assert!(a.separate_time(&b, 0.1) == 0.0);

        b.shape.shape == Shape::new_circle(2.0);
        b.vel.shape == Shape::new_circle(0.0);
        assert!(a.collide_time(&b) == f64::INFINITY);
        assert!(a.separate_time(&b, 0.1) == 0.0);

        a.shape.shape == Shape::new_circle(2.0);
        a.vel.shape == Shape::new_circle(0.0);
        assert!(a.collide_time(&b) == f64::INFINITY);
        assert!(a.separate_time(&b, 0.1) == 0.0);
    }

    #[test]
    fn test_no_separation() {
        let mut a = Hitbox::new(PlacedShape::new(Vec2::new(5.0, 1.0), Shape::new_rect(2.0, 2.0)));
        a.vel.pos = Vec2::new(2.0, 1.0);
        a.duration = 100.0;
        let mut b = Hitbox::new(PlacedShape::new(Vec2::new(5.0, 1.0), Shape::new_rect(2.0, 4.0)));
        b.vel.pos = Vec2::new(2.0, 1.0);
        b.duration = 100.0;
        assert!(a.separate_time(&b, 0.1) == f64::INFINITY);
        assert!(a.collide_time(&b) == 0.0);

        b.shape.shape == Shape::new_circle(2.0);
        b.vel.shape == Shape::new_circle(0.0);
        assert!(a.separate_time(&b, 0.1) == f64::INFINITY);
        assert!(a.collide_time(&b) == 0.0);

        a.shape.shape == Shape::new_circle(2.0);
        a.vel.shape == Shape::new_circle(0.0);
        assert!(a.separate_time(&b, 0.1) == f64::INFINITY);
        assert!(a.collide_time(&b) == 0.0);
    }

    #[test]
    fn test_low_duration() {
        let sqrt2 = (2.0f64).sqrt();
        let mut a = Hitbox::new(PlacedShape::new(Vec2::new(0.0, 0.0), Shape::new_circle(2.0)));
        a.duration = 4.0 - sqrt2 + 0.01;
        let mut b = Hitbox::new(PlacedShape::new(Vec2::new(4.0, 4.0), Shape::new_circle(2.0)));
        b.vel.pos = Vec2::new(-1.0, -1.0);
        b.duration = 4.0 - sqrt2 + 0.01;
        assert!(a.collide_time(&b) == 4.0 - sqrt2);
        a.duration -= 0.02;
        assert!(a.collide_time(&b) == f64::INFINITY);
        b.duration -= 0.02;
        assert!(a.collide_time(&b) == f64::INFINITY);
    }
}
<commit_msg>documented Hitbox and interactivity<commit_after>// Copyright 2016 Matthew D. Michelotti
//
// 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.

mod solvers;
mod grid;
mod collider;
mod events;

pub use self::collider::*;

use geom::*;
use geom_ext::*;
use std::f64;

const HIGH_TIME: f64 = 1e50;

/// Type used as a handle for referencing hitboxes in a `Collider` instance.
pub type HitboxId = u64;

/// Represents a moving shape for continuous collision testing.
#[derive(PartialEq, Clone, Debug)]
pub struct Hitbox {
    /// The placed shape `shape` at the given point in time.
    pub shape: PlacedShape,

    /// A velocity that describes how the shape is changing over time.
    ///
    /// The `vel` may include the velocity of the width and height of the `shape` as
    /// well as the velocity of the position.
    pub vel: PlacedShape,

    /// An upper-bound on the time until this hitbox will be updated by the user.
    ///
    /// `f64::INFINITY` may be used as a default, but using a lower value may
    /// reduce the number of collisions that need to be checked.
    /// E.g. if you are updating the velocities of a hitbox once every second,
    /// then use a duration of one second when you update the hitbox.
    ///
    /// `Collider` will panic if the duration is exceeded without update.
    pub duration: f64
}

impl Hitbox {
    /// Constructs a new hitbox with the given `shape` and a `vel` of zero and `duration` of `f64::INFINITY`.
    pub fn new(shape: PlacedShape) -> Hitbox {
        Hitbox {
            shape : shape,
            vel : PlacedShape::new(Vec2::zero(), Shape::new(shape.kind(), 0.0, 0.0)),
            duration : f64::INFINITY
        }
    }

    fn advance(&mut self, orig_time: f64, new_time: f64) {
        assert!(orig_time <= new_time, "requires orig_time <= new_time");
        let delta = new_time - orig_time;
        if delta != 0.0 {
            self.shape = self.advanced_shape(delta);
            let end_time = orig_time + self.duration;
            assert!(new_time <= end_time, "tried to advance Hitbox beyond its duration");
            self.duration = end_time - new_time;
        }
    }

    fn advanced_shape(&self, time: f64) -> PlacedShape {
        assert!(time <= HIGH_TIME, "requires time <= {}", HIGH_TIME);
        self.shape + self.vel*time
    }

    fn bounding_box(&self) -> PlacedShape {
        self.bounding_box_for(self.duration)
    }

    fn bounding_box_for(&self, duration: f64) -> PlacedShape {
        if self.vel.is_zero() {
            self.shape.as_rect()
        } else {
            let end_shape = self.advanced_shape(duration);
            self.shape.bounding_box(&end_shape)
        }
    }

    fn collide_time(&self, other: &Hitbox) -> f64 {
        solvers::collide_time(self, other)
    }

    fn separate_time(&self, other: &Hitbox, padding: f64) -> f64 {
        solvers::separate_time(self, other, padding)
    }

    fn validate(&self, min_size: f64) {
        assert!(!self.duration.is_nan() && self.duration >= 0.0, "duration must be non-negative");
        assert!(self.shape.kind() == self.vel.kind(), "shape and vel have different kinds");
        assert!(self.shape.width() >= min_size && self.shape.height() >= min_size, "shape width/height must be at least {}", min_size);
    }

    fn time_until_too_small(&self, min_size: f64) -> f64 {
        let min_size = min_size*0.9;
        assert!(self.shape.width() > min_size && self.shape.height() > min_size, "illegal state");
        let mut time = f64::INFINITY;
        if self.vel.width() < 0.0 { time = time.min((min_size - self.shape.width())/self.vel.width()); }
        if self.vel.height() < 0.0 { time = time.min((min_size - self.shape.height())/self.vel.height()); }
        time
    }
}

/// Contains types that describe interactions between hitboxes.
pub mod inter {
    /// A group id that may be used as a first measure to efficiently filter out hitboxes that don't interact.
    ///
    /// The total number of groups used should in general be very small.
    /// Often 1 is enough, and 4 is excessive.
    /// As an example, in a [danmaku](https://en.wikipedia.org/wiki/Shoot_%27em_up#Bullet_hell_and_niche_appeal) game
    /// (which has many bullets on screen that do not interact with each other),
    /// we may use one group for bullets and one group for everything else,
    /// to avoid the quadratic cost of comparing all nearby bullets with each other.
    pub type Group = u32;

    static DEFAULT_GROUPS: [Group; 1] = [0];

    /// Used to determine which pairs of hitboxes should be checked for collisions
    /// and which pairs should be ignored.
    pub trait Interactivity {
        /// Returns the group id associated with the hitbox.
        /// Default is `Some(0)`.
        ///
        /// If `None` is returned, then no collisions will be reported
        /// for this hitbox at all.
        fn group(&self) -> Option<Group> { Some(0) }

        /// Returns a list of groups that this hitbox can interact with.
        /// Using large lists of groups may be inefficient.
        /// Default is `[0]`.
        fn interact_groups(&self) -> &'static [Group] { &DEFAULT_GROUPS }

        /// Returns true if the pair of hitboxes should be checked for collisions.
        /// This method should be commutative.
        /// This method should be consistent with `group` and `interact_groups`,
        /// although possibly more restrictive.
        fn can_interact(&self, other: &Self) -> bool;
    }

    /// The default implementation of `Interactivity`, in which
    /// every hitbox is allowed to interact with every other hitbox.
    #[derive(Default)]
    pub struct DefaultInteractivity;

    impl Interactivity for DefaultInteractivity {
        fn can_interact(&self, _other: &Self) -> bool { true }
    }
}

#[cfg(test)]
mod tests {
    use geom::*;
    use core::*;
    use std::f64;

    #[test]
    fn test_rect_rect_collision() {
        let mut a = Hitbox::new(PlacedShape::new(Vec2::new(-11.0, 0.0), Shape::new_rect(2.0, 2.0)));
        a.vel.pos = Vec2::new(2.0, 0.0);
        a.duration = 100.0;
        let mut b = Hitbox::new(PlacedShape::new(Vec2::new(12.0, 2.0), Shape::new_rect(2.0, 4.0)));
        b.vel.pos = Vec2::new(-0.5, 0.0);
        b.vel.shape = Shape::new_rect(1.0, 0.0);
        b.duration = 100.0;
        assert!(a.collide_time(&b) == 7.0);
        assert!(b.collide_time(&a) == 7.0);
        assert!(a.separate_time(&b, 0.1) == 0.0);
    }

    #[test]
    fn test_circle_circle_collision() {
        let sqrt2 = (2.0f64).sqrt();
        let mut a = Hitbox::new(PlacedShape::new(Vec2::new(-0.1*sqrt2, 0.0), Shape::new_circle(2.0)));
        a.vel.pos = Vec2::new(0.1, 0.0);
        a.duration = 100.0;
        let mut b = Hitbox::new(PlacedShape::new(Vec2::new(3.0*sqrt2, 0.0), Shape::new_circle(2.0 + sqrt2*0.1)));
        b.vel.pos = Vec2::new(-2.0, 1.0);
        b.vel.shape = Shape::new_circle(-0.1);
        b.duration = 100.0;
        assert!((a.collide_time(&b) - sqrt2).abs() < 1e-7);
        assert!(a.separate_time(&b, 0.1) == 0.0);
    }

    #[test]
    fn test_rect_circle_collision() {
        let mut a = Hitbox::new(PlacedShape::new(Vec2::new(-11.0, 0.0), Shape::new_circle(2.0)));
        a.vel.pos = Vec2::new(2.0, 0.0);
        a.duration = 100.0;
        let mut b = Hitbox::new(PlacedShape::new(Vec2::new(12.0, 2.0), Shape::new_rect(2.0, 4.0)));
        b.vel.pos = Vec2::new(-1.0, 0.0);
        b.duration = 100.0;
        assert!(a.collide_time(&b) == 7.0);
        assert!(b.collide_time(&a) == 7.0);
        assert!(a.separate_time(&b, 0.1) == 0.0);
    }

    #[test]
    fn test_rect_rect_separation() {
        let mut a = Hitbox::new(PlacedShape::new(Vec2::new(0.0, 0.0), Shape::new_rect(6.0, 4.0)));
        a.vel.pos = Vec2::new(1.0, 1.0);
        a.duration = 100.0;
        let mut b = Hitbox::new(PlacedShape::new(Vec2::new(1.0, 0.0), Shape::new_rect(4.0, 4.0)));
        b.vel.pos = Vec2::new(0.5, 0.0);
        b.duration = 100.0;
        assert!(a.separate_time(&b, 0.1) == 4.1);
        assert!(b.separate_time(&a, 0.1) == 4.1);
        assert!(a.collide_time(&b) == 0.0);
    }

    #[test]
    fn test_circle_circle_separation() {
        let sqrt2 = (2.0f64).sqrt();
        let mut a = Hitbox::new(PlacedShape::new(Vec2::new(2.0, 5.0), Shape::new_circle(2.0)));
        a.duration = 100.0;
        let mut b = Hitbox::new(PlacedShape::new(Vec2::new(3.0, 4.0), Shape::new_circle(1.8)));
        b.vel.pos = Vec2::new(-1.0, 1.0);
        b.duration = 100.0;
        assert!(a.separate_time(&b, 0.1) == 1.0 + sqrt2);
        assert!(b.separate_time(&a, 0.1) == 1.0 + sqrt2);
        assert!(a.collide_time(&b) == 0.0);
    }

    #[test]
    fn test_rect_circle_separation() {
        let sqrt2 = (2.0f64).sqrt();
        let mut a = Hitbox::new(PlacedShape::new(Vec2::new(4.0, 2.0), Shape::new_rect(4.0, 6.0)));
        a.duration = 100.0;
        let mut b = Hitbox::new(PlacedShape::new(Vec2::new(3.0, 4.0), Shape::new_circle(3.8)));
        b.vel.pos = Vec2::new(-1.0, 1.0);
        b.duration = 100.0;
        assert!(a.separate_time(&b, 0.1) == 1.0 + sqrt2);
        assert!(b.separate_time(&a, 0.1) == 1.0 + sqrt2);
        assert!(a.collide_time(&b) == 0.0);
    }

    #[test]
    fn test_no_collision() {
        let mut a = Hitbox::new(PlacedShape::new(Vec2::new(-11.0, 0.0), Shape::new_rect(2.0, 2.0)));
        a.vel.pos = Vec2::new(2.0, 0.0);
        a.duration = 100.0;
        let mut b = Hitbox::new(PlacedShape::new(Vec2::new(12.0, 2.0), Shape::new_rect(2.0, 4.0)));
        b.vel.pos = Vec2::new(-1.0, 1.0);
        b.duration = 100.0;
        assert!(a.collide_time(&b) == f64::INFINITY);
        assert!(a.separate_time(&b, 0.1) == 0.0);

        b.shape.shape == Shape::new_circle(2.0);
        b.vel.shape == Shape::new_circle(0.0);
        assert!(a.collide_time(&b) == f64::INFINITY);
        assert!(a.separate_time(&b, 0.1) == 0.0);

        a.shape.shape == Shape::new_circle(2.0);
        a.vel.shape == Shape::new_circle(0.0);
        assert!(a.collide_time(&b) == f64::INFINITY);
        assert!(a.separate_time(&b, 0.1) == 0.0);
    }

    #[test]
    fn test_no_separation() {
        let mut a = Hitbox::new(PlacedShape::new(Vec2::new(5.0, 1.0), Shape::new_rect(2.0, 2.0)));
        a.vel.pos = Vec2::new(2.0, 1.0);
        a.duration = 100.0;
        let mut b = Hitbox::new(PlacedShape::new(Vec2::new(5.0, 1.0), Shape::new_rect(2.0, 4.0)));
        b.vel.pos = Vec2::new(2.0, 1.0);
        b.duration = 100.0;
        assert!(a.separate_time(&b, 0.1) == f64::INFINITY);
        assert!(a.collide_time(&b) == 0.0);

        b.shape.shape == Shape::new_circle(2.0);
        b.vel.shape == Shape::new_circle(0.0);
        assert!(a.separate_time(&b, 0.1) == f64::INFINITY);
        assert!(a.collide_time(&b) == 0.0);

        a.shape.shape == Shape::new_circle(2.0);
        a.vel.shape == Shape::new_circle(0.0);
        assert!(a.separate_time(&b, 0.1) == f64::INFINITY);
        assert!(a.collide_time(&b) == 0.0);
    }

    #[test]
    fn test_low_duration() {
        let sqrt2 = (2.0f64).sqrt();
        let mut a = Hitbox::new(PlacedShape::new(Vec2::new(0.0, 0.0), Shape::new_circle(2.0)));
        a.duration = 4.0 - sqrt2 + 0.01;
        let mut b = Hitbox::new(PlacedShape::new(Vec2::new(4.0, 4.0), Shape::new_circle(2.0)));
        b.vel.pos = Vec2::new(-1.0, -1.0);
        b.duration = 4.0 - sqrt2 + 0.01;
        assert!(a.collide_time(&b) == 4.0 - sqrt2);
        a.duration -= 0.02;
        assert!(a.collide_time(&b) == f64::INFINITY);
        b.duration -= 0.02;
        assert!(a.collide_time(&b) == f64::INFINITY);
    }
}
<|endoftext|>


<commit_before>#[allow(dead_code)]
#[repr(C)]
pub enum Error {
    Success = 0,
    ErrorInvalidValue = 1,
}

#[allow(dead_code)]
#[repr(C)]
pub enum MemcpyKind {
    HostToHost = 0,
    HostToDevice = 1,
    DeviceToHost = 2,
    DeviceToDevice = 3,
    Default = 4
}

extern "C" {
    pub fn cudaMalloc(devPtr: *mut *mut ::libc::c_void,
                      size: ::libc::c_ulong)
        -> Error;

    pub fn cudaMemcpy(dst: *mut ::libc::c_void,
                      src: *const ::libc::c_void,
                      count: ::libc::c_ulong,
                      kind: MemcpyKind)
        -> Error;
}
<commit_msg>added error codes for cuda<commit_after>#[allow(dead_code)]
#[repr(C)]
pub enum Error {
    Success = 0,
    ErrorInvalidValue = 1,
    OutOfMemory = 2,
    NotInitialized = 3,
    Deinitialized = 4,
    CUDA_ERROR_PROFILER_DISABLED              = 5,
    CUDA_ERROR_PROFILER_NOT_INITIALIZED       = 6,
    CUDA_ERROR_PROFILER_ALREADY_STARTED       = 7,
    CUDA_ERROR_PROFILER_ALREADY_STOPPED       = 8,
    CUDA_ERROR_NO_DEVICE                      = 100,
    CUDA_ERROR_INVALID_DEVICE                 = 101,
    CUDA_ERROR_INVALID_IMAGE                  = 200,
    CUDA_ERROR_INVALID_CONTEXT                = 201,
    CUDA_ERROR_CONTEXT_ALREADY_CURRENT        = 202,
    CUDA_ERROR_MAP_FAILED                     = 205,
    CUDA_ERROR_UNMAP_FAILED                   = 206,
    CUDA_ERROR_ARRAY_IS_MAPPED                = 207,
    CUDA_ERROR_ALREADY_MAPPED                 = 208,
    CUDA_ERROR_NO_BINARY_FOR_GPU              = 209,
    CUDA_ERROR_ALREADY_ACQUIRED               = 210,
    CUDA_ERROR_NOT_MAPPED                     = 211,
    CUDA_ERROR_NOT_MAPPED_AS_ARRAY            = 212,
    CUDA_ERROR_NOT_MAPPED_AS_POINTER          = 213,
    CUDA_ERROR_ECC_UNCORRECTABLE              = 214,
    CUDA_ERROR_UNSUPPORTED_LIMIT              = 215,
    CUDA_ERROR_CONTEXT_ALREADY_IN_USE         = 216,
    CUDA_ERROR_PEER_ACCESS_UNSUPPORTED        = 217,
    CUDA_ERROR_INVALID_PTX                    = 218,
    CUDA_ERROR_INVALID_GRAPHICS_CONTEXT       = 219,
    CUDA_ERROR_INVALID_SOURCE                 = 300,
    CUDA_ERROR_FILE_NOT_FOUND                 = 301,
    CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND = 302,
    CUDA_ERROR_SHARED_OBJECT_INIT_FAILED      = 303,
    CUDA_ERROR_OPERATING_SYSTEM               = 304,
    CUDA_ERROR_INVALID_HANDLE                 = 400,
    CUDA_ERROR_NOT_FOUND                      = 500,
    CUDA_ERROR_NOT_READY                      = 600,
    CUDA_ERROR_ILLEGAL_ADDRESS                = 700,
    CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES        = 701,
    * ::CUDA_ERROR_LAUNCH_FAILED). All existing device memory allocations from
    CUDA_ERROR_LAUNCH_TIMEOUT                 = 702,
CUDA_ERROR_LAUNCH_INCOMPATIBLE_TEXTURING  = 703,
CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED    = 704,
CUDA_ERROR_PEER_ACCESS_NOT_ENABLED        = 705,
CUDA_ERROR_PRIMARY_CONTEXT_ACTIVE         = 708,
CUDA_ERROR_CONTEXT_IS_DESTROYED           = 709,
CUDA_ERROR_ASSERT                         = 710,
CUDA_ERROR_TOO_MANY_PEERS                 = 711,
CUDA_ERROR_HOST_MEMORY_ALREADY_REGISTERED = 712,
CUDA_ERROR_HOST_MEMORY_NOT_REGISTERED     = 713,
CUDA_ERROR_HARDWARE_STACK_ERROR           = 714,
CUDA_ERROR_ILLEGAL_INSTRUCTION            = 715,
CUDA_ERROR_MISALIGNED_ADDRESS             = 716,
CUDA_ERROR_INVALID_ADDRESS_SPACE          = 717,
CUDA_ERROR_INVALID_PC                     = 718,
CUDA_ERROR_LAUNCH_FAILED                  = 719,
CUDA_ERROR_NOT_PERMITTED                  = 800,
CUDA_ERROR_NOT_SUPPORTED                  = 801,
    CUDA_ERROR_UNKNOWN                        = 999
}

#[allow(dead_code)]
#[repr(C)]
pub enum MemcpyKind {
    HostToHost = 0,
    HostToDevice = 1,
    DeviceToHost = 2,
    DeviceToDevice = 3,
    Default = 4
}

extern "C" {
    pub fn cudaMalloc(devPtr: *mut *mut ::libc::c_void,
                      size: ::libc::c_ulong)
        -> Error;

    pub fn cudaMemcpy(dst: *mut ::libc::c_void,
                      src: *const ::libc::c_void,
                      count: ::libc::c_ulong,
                      kind: MemcpyKind)
        -> Error;
}
<|endoftext|>


<commit_before>/* 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 api::{BorderRadius, ClipMode, HitTestFlags, HitTestItem, HitTestResult, ItemTag, LayoutPoint};
use api::{LayoutPrimitiveInfo, LayoutRect, PipelineId, WorldPoint};
use clip::{ClipSource, ClipStore, rounded_rectangle_contains_point};
use clip_node::ClipNode;
use clip_scroll_tree::{ClipChainIndex, ClipNodeIndex, SpatialNodeIndex, ClipScrollTree};
use internal_types::FastHashMap;
use prim_store::ScrollNodeAndClipChain;
use util::LayoutToWorldFastTransform;

/// A copy of important clip scroll node data to use during hit testing. This a copy of
/// data from the ClipScrollTree that will persist as a new frame is under construction,
/// allowing hit tests consistent with the currently rendered frame.
pub struct HitTestSpatialNode {
    /// The pipeline id of this node.
    pipeline_id: PipelineId,

    /// World transform for content transformed by this node.
    world_content_transform: LayoutToWorldFastTransform,

    /// World viewport transform for content transformed by this node.
    world_viewport_transform: LayoutToWorldFastTransform,
}

pub struct HitTestClipNode {
    /// The positioning node for this clip node.
    spatial_node: SpatialNodeIndex,

    /// A particular point must be inside all of these regions to be considered clipped in
    /// for the purposes of a hit test.
    regions: Vec<HitTestRegion>,
}

/// A description of a clip chain in the HitTester. This is used to describe
/// hierarchical clip scroll nodes as well as ClipChains, so that they can be
/// handled the same way during hit testing. Once we represent all ClipChains
/// using ClipChainDescriptors, we can get rid of this and just use the
/// ClipChainDescriptor here.
#[derive(Clone)]
struct HitTestClipChainDescriptor {
    parent: Option<ClipChainIndex>,
    clips: Vec<ClipNodeIndex>,
}

impl HitTestClipChainDescriptor {
    fn empty() -> HitTestClipChainDescriptor {
        HitTestClipChainDescriptor {
            parent: None,
            clips: Vec::new(),
        }
    }
}

#[derive(Clone)]
pub struct HitTestingItem {
    rect: LayoutRect,
    clip_rect: LayoutRect,
    tag: ItemTag,
    is_backface_visible: bool,
}

impl HitTestingItem {
    pub fn new(tag: ItemTag, info: &LayoutPrimitiveInfo) -> HitTestingItem {
        HitTestingItem {
            rect: info.rect,
            clip_rect: info.clip_rect,
            tag,
            is_backface_visible: info.is_backface_visible,
        }
    }
}

#[derive(Clone)]
pub struct HitTestingRun(pub Vec<HitTestingItem>, pub ScrollNodeAndClipChain);

enum HitTestRegion {
    Rectangle(LayoutRect, ClipMode),
    RoundedRectangle(LayoutRect, BorderRadius, ClipMode),
}

impl HitTestRegion {
    pub fn contains(&self, point: &LayoutPoint) -> bool {
        match *self {
            HitTestRegion::Rectangle(ref rectangle, ClipMode::Clip) =>
                rectangle.contains(point),
            HitTestRegion::Rectangle(ref rectangle, ClipMode::ClipOut) =>
                !rectangle.contains(point),
            HitTestRegion::RoundedRectangle(rect, radii, ClipMode::Clip) =>
                rounded_rectangle_contains_point(point, &rect, &radii),
            HitTestRegion::RoundedRectangle(rect, radii, ClipMode::ClipOut) =>
                !rounded_rectangle_contains_point(point, &rect, &radii),
        }
    }
}

pub struct HitTester {
    runs: Vec<HitTestingRun>,
    spatial_nodes: Vec<HitTestSpatialNode>,
    clip_nodes: Vec<HitTestClipNode>,
    clip_chains: Vec<HitTestClipChainDescriptor>,
    pipeline_root_nodes: FastHashMap<PipelineId, SpatialNodeIndex>,
}

impl HitTester {
    pub fn new(
        runs: &Vec<HitTestingRun>,
        clip_scroll_tree: &ClipScrollTree,
        clip_store: &ClipStore
    ) -> HitTester {
        let mut hit_tester = HitTester {
            runs: runs.clone(),
            spatial_nodes: Vec::new(),
            clip_nodes: Vec::new(),
            clip_chains: Vec::new(),
            pipeline_root_nodes: FastHashMap::default(),
        };
        hit_tester.read_clip_scroll_tree(clip_scroll_tree, clip_store);
        hit_tester
    }

    fn read_clip_scroll_tree(
        &mut self,
        clip_scroll_tree: &ClipScrollTree,
        clip_store: &ClipStore
    ) {
        self.spatial_nodes.clear();
        self.clip_chains.clear();
        self.clip_chains.resize(
            clip_scroll_tree.clip_chains.len(),
            HitTestClipChainDescriptor::empty()
        );

        for (index, node) in clip_scroll_tree.spatial_nodes.iter().enumerate() {
            let index = SpatialNodeIndex(index);

            // If we haven't already seen a node for this pipeline, record this one as the root
            // node.
            self.pipeline_root_nodes.entry(node.pipeline_id).or_insert(index);

            self.spatial_nodes.push(HitTestSpatialNode {
                pipeline_id: node.pipeline_id,
                world_content_transform: node.world_content_transform,
                world_viewport_transform: node.world_viewport_transform,
            });
        }

        for (index, node) in clip_scroll_tree.clip_nodes.iter().enumerate() {
            let regions = match get_regions_for_clip_node(node, clip_store) {
                Some(regions) => regions,
                None => continue,
            };
            self.clip_nodes.push(HitTestClipNode {
                spatial_node: node.spatial_node,
                regions,
            });

             let clip_chain = self.clip_chains.get_mut(node.clip_chain_index.0).unwrap();
             clip_chain.parent =
                 clip_scroll_tree.get_clip_chain(node.clip_chain_index).parent_index;
             clip_chain.clips = vec![ClipNodeIndex(index)];
        }

        for descriptor in &clip_scroll_tree.clip_chains_descriptors {
            let clip_chain = self.clip_chains.get_mut(descriptor.index.0).unwrap();
            clip_chain.parent = clip_scroll_tree.get_clip_chain(descriptor.index).parent_index;
            clip_chain.clips = descriptor.clips.clone();
        }
    }

    fn is_point_clipped_in_for_clip_chain(
        &self,
        point: WorldPoint,
        clip_chain_index: ClipChainIndex,
        test: &mut HitTest
    ) -> bool {
        if let Some(result) = test.get_from_clip_chain_cache(clip_chain_index) {
            return result == ClippedIn::ClippedIn;
        }

        let descriptor = &self.clip_chains[clip_chain_index.0];
        let parent_clipped_in = match descriptor.parent {
            None => true,
            Some(parent) => self.is_point_clipped_in_for_clip_chain(point, parent, test),
        };

        if !parent_clipped_in {
            test.set_in_clip_chain_cache(clip_chain_index, ClippedIn::NotClippedIn);
            return false;
        }

        for clip_node_index in &descriptor.clips {
            if !self.is_point_clipped_in_for_clip_node(point, *clip_node_index, test) {
                test.set_in_clip_chain_cache(clip_chain_index, ClippedIn::NotClippedIn);
                return false;
            }
        }

        test.set_in_clip_chain_cache(clip_chain_index, ClippedIn::ClippedIn);
        true
    }

    fn is_point_clipped_in_for_clip_node(
        &self,
        point: WorldPoint,
        node_index: ClipNodeIndex,
        test: &mut HitTest
    ) -> bool {
        if let Some(clipped_in) = test.node_cache.get(&node_index) {
            return *clipped_in == ClippedIn::ClippedIn;
        }

        let node = &self.clip_nodes[node_index.0];
        let transform = self.spatial_nodes[node.spatial_node.0].world_viewport_transform;
        let transformed_point = match transform.inverse() {
            Some(inverted) => inverted.transform_point2d(&point),
            None => {
                test.node_cache.insert(node_index, ClippedIn::NotClippedIn);
                return false;
            }
        };

        for region in &node.regions {
            if !region.contains(&transformed_point) {
                test.node_cache.insert(node_index, ClippedIn::NotClippedIn);
                return false;
            }
        }

        test.node_cache.insert(node_index, ClippedIn::ClippedIn);
        true
    }

    pub fn find_node_under_point(&self, mut test: HitTest) -> Option<SpatialNodeIndex> {
        let point = test.get_absolute_point(self);

        for &HitTestingRun(ref items, ref clip_and_scroll) in self.runs.iter().rev() {
            let scroll_node_id = clip_and_scroll.scroll_node_id;
            let scroll_node = &self.spatial_nodes[scroll_node_id.0];
            let transform = scroll_node.world_content_transform;
            let point_in_layer = match transform.inverse() {
                Some(inverted) => inverted.transform_point2d(&point),
                None => continue,
            };

            let mut clipped_in = false;
            for item in items.iter().rev() {
                if !item.rect.contains(&point_in_layer) ||
                    !item.clip_rect.contains(&point_in_layer) {
                    continue;
                }

                let clip_chain_index = clip_and_scroll.clip_chain_index;
                clipped_in |=
                    self.is_point_clipped_in_for_clip_chain(point, clip_chain_index, &mut test);
                if !clipped_in {
                    break;
                }

                return Some(scroll_node_id);
            }
        }

        None
    }

    pub fn hit_test(&self, mut test: HitTest) -> HitTestResult {
        let point = test.get_absolute_point(self);

        let mut result = HitTestResult::default();
        for &HitTestingRun(ref items, ref clip_and_scroll) in self.runs.iter().rev() {
            let scroll_node_id = clip_and_scroll.scroll_node_id;
            let scroll_node = &self.spatial_nodes[scroll_node_id.0];
            let pipeline_id = scroll_node.pipeline_id;
            match (test.pipeline_id, pipeline_id) {
                (Some(id), node_id) if node_id != id => continue,
                _ => {},
            }

            let transform = scroll_node.world_content_transform;
            let mut facing_backwards: Option<bool> = None;  // will be computed on first use
            let point_in_layer = match transform.inverse() {
                Some(inverted) => inverted.transform_point2d(&point),
                None => continue,
            };

            let mut clipped_in = false;
            for item in items.iter().rev() {
                if !item.rect.contains(&point_in_layer) ||
                    !item.clip_rect.contains(&point_in_layer) {
                    continue;
                }

                let clip_chain_index = clip_and_scroll.clip_chain_index;
                clipped_in = clipped_in ||
                    self.is_point_clipped_in_for_clip_chain(point, clip_chain_index, &mut test);
                if !clipped_in {
                    break;
                }

                // Don't hit items with backface-visibility:hidden if they are facing the back.
                if !item.is_backface_visible {
                    if *facing_backwards.get_or_insert_with(|| transform.is_backface_visible()) {
                        continue;
                    }
                }

                // We need to calculate the position of the test point relative to the origin of
                // the pipeline of the hit item. If we cannot get a transformed point, we are
                // in a situation with an uninvertible transformation so we should just skip this
                // result.
                let root_node = &self.spatial_nodes[self.pipeline_root_nodes[&pipeline_id].0];
                let point_in_viewport = match root_node.world_viewport_transform.inverse() {
                    Some(inverted) => inverted.transform_point2d(&point),
                    None => continue,
                };

                result.items.push(HitTestItem {
                    pipeline: pipeline_id,
                    tag: item.tag,
                    point_in_viewport,
                    point_relative_to_item: point_in_layer - item.rect.origin.to_vector(),
                });
                if !test.flags.contains(HitTestFlags::FIND_ALL) {
                    return result;
                }
            }
        }

        result.items.dedup();
        result
    }

    pub fn get_pipeline_root(&self, pipeline_id: PipelineId) -> &HitTestSpatialNode {
        &self.spatial_nodes[self.pipeline_root_nodes[&pipeline_id].0]
    }
}

fn get_regions_for_clip_node(
    node: &ClipNode,
    clip_store: &ClipStore
) -> Option<Vec<HitTestRegion>> {
    let handle = match node.handle.as_ref() {
        Some(handle) => handle,
        None => {
            warn!("Encountered an empty clip node unexpectedly.");
            return None;
        }
    };

    let clips = clip_store.get(handle).clips();
    Some(clips.iter().map(|source| {
        match source.0 {
            ClipSource::Rectangle(ref rect, mode) => HitTestRegion::Rectangle(*rect, mode),
            ClipSource::RoundedRectangle(ref rect, ref radii, ref mode) =>
                HitTestRegion::RoundedRectangle(*rect, *radii, *mode),
            ClipSource::Image(ref mask) => HitTestRegion::Rectangle(mask.rect, ClipMode::Clip),
            ClipSource::LineDecoration(_) |
            ClipSource::BoxShadow(_) => {
                unreachable!("Didn't expect to hit test against BorderCorner / BoxShadow / LineDecoration");
            }
        }
    }).collect())
}

#[derive(Clone, Copy, PartialEq)]
enum ClippedIn {
    ClippedIn,
    NotClippedIn,
}

pub struct HitTest {
    pipeline_id: Option<PipelineId>,
    point: WorldPoint,
    flags: HitTestFlags,
    node_cache: FastHashMap<ClipNodeIndex, ClippedIn>,
    clip_chain_cache: Vec<Option<ClippedIn>>,
}

impl HitTest {
    pub fn new(
        pipeline_id: Option<PipelineId>,
        point: WorldPoint,
        flags: HitTestFlags,
    ) -> HitTest {
        HitTest {
            pipeline_id,
            point,
            flags,
            node_cache: FastHashMap::default(),
            clip_chain_cache: Vec::new(),
        }
    }

    fn get_from_clip_chain_cache(&mut self, index: ClipChainIndex) -> Option<ClippedIn> {
        if index.0 >= self.clip_chain_cache.len() {
            None
        } else {
            self.clip_chain_cache[index.0]
        }
    }

    fn set_in_clip_chain_cache(&mut self, index: ClipChainIndex, value: ClippedIn) {
        if index.0 >= self.clip_chain_cache.len() {
            self.clip_chain_cache.resize(index.0 + 1, None);
        }
        self.clip_chain_cache[index.0] = Some(value);
    }

    fn get_absolute_point(&self, hit_tester: &HitTester) -> WorldPoint {
        if !self.flags.contains(HitTestFlags::POINT_RELATIVE_TO_PIPELINE_VIEWPORT) {
            return self.point;
        }

        let point =  &LayoutPoint::new(self.point.x, self.point.y);
        self.pipeline_id.map(|id|
            hit_tester.get_pipeline_root(id).world_viewport_transform.transform_point2d(point)
        ).unwrap_or_else(|| WorldPoint::new(self.point.x, self.point.y))
    }
}
<commit_msg>Auto merge of #2899 - mrobinson:fix-empty-hit-testing-clip-nodes, r=kvark<commit_after>/* 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 api::{BorderRadius, ClipMode, HitTestFlags, HitTestItem, HitTestResult, ItemTag, LayoutPoint};
use api::{LayoutPrimitiveInfo, LayoutRect, PipelineId, WorldPoint};
use clip::{ClipSource, ClipStore, rounded_rectangle_contains_point};
use clip_node::ClipNode;
use clip_scroll_tree::{ClipChainIndex, ClipNodeIndex, SpatialNodeIndex, ClipScrollTree};
use internal_types::FastHashMap;
use prim_store::ScrollNodeAndClipChain;
use util::LayoutToWorldFastTransform;

/// A copy of important clip scroll node data to use during hit testing. This a copy of
/// data from the ClipScrollTree that will persist as a new frame is under construction,
/// allowing hit tests consistent with the currently rendered frame.
pub struct HitTestSpatialNode {
    /// The pipeline id of this node.
    pipeline_id: PipelineId,

    /// World transform for content transformed by this node.
    world_content_transform: LayoutToWorldFastTransform,

    /// World viewport transform for content transformed by this node.
    world_viewport_transform: LayoutToWorldFastTransform,
}

pub struct HitTestClipNode {
    /// The positioning node for this clip node.
    spatial_node: SpatialNodeIndex,

    /// A particular point must be inside all of these regions to be considered clipped in
    /// for the purposes of a hit test.
    regions: Vec<HitTestRegion>,
}

/// A description of a clip chain in the HitTester. This is used to describe
/// hierarchical clip scroll nodes as well as ClipChains, so that they can be
/// handled the same way during hit testing. Once we represent all ClipChains
/// using ClipChainDescriptors, we can get rid of this and just use the
/// ClipChainDescriptor here.
#[derive(Clone)]
struct HitTestClipChainDescriptor {
    parent: Option<ClipChainIndex>,
    clips: Vec<ClipNodeIndex>,
}

impl HitTestClipChainDescriptor {
    fn empty() -> HitTestClipChainDescriptor {
        HitTestClipChainDescriptor {
            parent: None,
            clips: Vec::new(),
        }
    }
}

#[derive(Clone)]
pub struct HitTestingItem {
    rect: LayoutRect,
    clip_rect: LayoutRect,
    tag: ItemTag,
    is_backface_visible: bool,
}

impl HitTestingItem {
    pub fn new(tag: ItemTag, info: &LayoutPrimitiveInfo) -> HitTestingItem {
        HitTestingItem {
            rect: info.rect,
            clip_rect: info.clip_rect,
            tag,
            is_backface_visible: info.is_backface_visible,
        }
    }
}

#[derive(Clone)]
pub struct HitTestingRun(pub Vec<HitTestingItem>, pub ScrollNodeAndClipChain);

enum HitTestRegion {
    Rectangle(LayoutRect, ClipMode),
    RoundedRectangle(LayoutRect, BorderRadius, ClipMode),
}

impl HitTestRegion {
    pub fn contains(&self, point: &LayoutPoint) -> bool {
        match *self {
            HitTestRegion::Rectangle(ref rectangle, ClipMode::Clip) =>
                rectangle.contains(point),
            HitTestRegion::Rectangle(ref rectangle, ClipMode::ClipOut) =>
                !rectangle.contains(point),
            HitTestRegion::RoundedRectangle(rect, radii, ClipMode::Clip) =>
                rounded_rectangle_contains_point(point, &rect, &radii),
            HitTestRegion::RoundedRectangle(rect, radii, ClipMode::ClipOut) =>
                !rounded_rectangle_contains_point(point, &rect, &radii),
        }
    }
}

pub struct HitTester {
    runs: Vec<HitTestingRun>,
    spatial_nodes: Vec<HitTestSpatialNode>,
    clip_nodes: Vec<HitTestClipNode>,
    clip_chains: Vec<HitTestClipChainDescriptor>,
    pipeline_root_nodes: FastHashMap<PipelineId, SpatialNodeIndex>,
}

impl HitTester {
    pub fn new(
        runs: &Vec<HitTestingRun>,
        clip_scroll_tree: &ClipScrollTree,
        clip_store: &ClipStore
    ) -> HitTester {
        let mut hit_tester = HitTester {
            runs: runs.clone(),
            spatial_nodes: Vec::new(),
            clip_nodes: Vec::new(),
            clip_chains: Vec::new(),
            pipeline_root_nodes: FastHashMap::default(),
        };
        hit_tester.read_clip_scroll_tree(clip_scroll_tree, clip_store);
        hit_tester
    }

    fn read_clip_scroll_tree(
        &mut self,
        clip_scroll_tree: &ClipScrollTree,
        clip_store: &ClipStore
    ) {
        self.spatial_nodes.clear();
        self.clip_chains.clear();
        self.clip_chains.resize(
            clip_scroll_tree.clip_chains.len(),
            HitTestClipChainDescriptor::empty()
        );

        for (index, node) in clip_scroll_tree.spatial_nodes.iter().enumerate() {
            let index = SpatialNodeIndex(index);

            // If we haven't already seen a node for this pipeline, record this one as the root
            // node.
            self.pipeline_root_nodes.entry(node.pipeline_id).or_insert(index);

            self.spatial_nodes.push(HitTestSpatialNode {
                pipeline_id: node.pipeline_id,
                world_content_transform: node.world_content_transform,
                world_viewport_transform: node.world_viewport_transform,
            });
        }

        for (index, node) in clip_scroll_tree.clip_nodes.iter().enumerate() {
            self.clip_nodes.push(HitTestClipNode {
                spatial_node: node.spatial_node,
                regions: get_regions_for_clip_node(node, clip_store),
            });

             let clip_chain = self.clip_chains.get_mut(node.clip_chain_index.0).unwrap();
             clip_chain.parent =
                 clip_scroll_tree.get_clip_chain(node.clip_chain_index).parent_index;
             clip_chain.clips = vec![ClipNodeIndex(index)];
        }

        for descriptor in &clip_scroll_tree.clip_chains_descriptors {
            let clip_chain = self.clip_chains.get_mut(descriptor.index.0).unwrap();
            clip_chain.parent = clip_scroll_tree.get_clip_chain(descriptor.index).parent_index;
            clip_chain.clips = descriptor.clips.clone();
        }
    }

    fn is_point_clipped_in_for_clip_chain(
        &self,
        point: WorldPoint,
        clip_chain_index: ClipChainIndex,
        test: &mut HitTest
    ) -> bool {
        if let Some(result) = test.get_from_clip_chain_cache(clip_chain_index) {
            return result == ClippedIn::ClippedIn;
        }

        let descriptor = &self.clip_chains[clip_chain_index.0];
        let parent_clipped_in = match descriptor.parent {
            None => true,
            Some(parent) => self.is_point_clipped_in_for_clip_chain(point, parent, test),
        };

        if !parent_clipped_in {
            test.set_in_clip_chain_cache(clip_chain_index, ClippedIn::NotClippedIn);
            return false;
        }

        for clip_node_index in &descriptor.clips {
            if !self.is_point_clipped_in_for_clip_node(point, *clip_node_index, test) {
                test.set_in_clip_chain_cache(clip_chain_index, ClippedIn::NotClippedIn);
                return false;
            }
        }

        test.set_in_clip_chain_cache(clip_chain_index, ClippedIn::ClippedIn);
        true
    }

    fn is_point_clipped_in_for_clip_node(
        &self,
        point: WorldPoint,
        node_index: ClipNodeIndex,
        test: &mut HitTest
    ) -> bool {
        if let Some(clipped_in) = test.node_cache.get(&node_index) {
            return *clipped_in == ClippedIn::ClippedIn;
        }

        let node = &self.clip_nodes[node_index.0];
        let transform = self.spatial_nodes[node.spatial_node.0].world_viewport_transform;
        let transformed_point = match transform.inverse() {
            Some(inverted) => inverted.transform_point2d(&point),
            None => {
                test.node_cache.insert(node_index, ClippedIn::NotClippedIn);
                return false;
            }
        };

        for region in &node.regions {
            if !region.contains(&transformed_point) {
                test.node_cache.insert(node_index, ClippedIn::NotClippedIn);
                return false;
            }
        }

        test.node_cache.insert(node_index, ClippedIn::ClippedIn);
        true
    }

    pub fn find_node_under_point(&self, mut test: HitTest) -> Option<SpatialNodeIndex> {
        let point = test.get_absolute_point(self);

        for &HitTestingRun(ref items, ref clip_and_scroll) in self.runs.iter().rev() {
            let scroll_node_id = clip_and_scroll.scroll_node_id;
            let scroll_node = &self.spatial_nodes[scroll_node_id.0];
            let transform = scroll_node.world_content_transform;
            let point_in_layer = match transform.inverse() {
                Some(inverted) => inverted.transform_point2d(&point),
                None => continue,
            };

            let mut clipped_in = false;
            for item in items.iter().rev() {
                if !item.rect.contains(&point_in_layer) ||
                    !item.clip_rect.contains(&point_in_layer) {
                    continue;
                }

                let clip_chain_index = clip_and_scroll.clip_chain_index;
                clipped_in |=
                    self.is_point_clipped_in_for_clip_chain(point, clip_chain_index, &mut test);
                if !clipped_in {
                    break;
                }

                return Some(scroll_node_id);
            }
        }

        None
    }

    pub fn hit_test(&self, mut test: HitTest) -> HitTestResult {
        let point = test.get_absolute_point(self);

        let mut result = HitTestResult::default();
        for &HitTestingRun(ref items, ref clip_and_scroll) in self.runs.iter().rev() {
            let scroll_node_id = clip_and_scroll.scroll_node_id;
            let scroll_node = &self.spatial_nodes[scroll_node_id.0];
            let pipeline_id = scroll_node.pipeline_id;
            match (test.pipeline_id, pipeline_id) {
                (Some(id), node_id) if node_id != id => continue,
                _ => {},
            }

            let transform = scroll_node.world_content_transform;
            let mut facing_backwards: Option<bool> = None;  // will be computed on first use
            let point_in_layer = match transform.inverse() {
                Some(inverted) => inverted.transform_point2d(&point),
                None => continue,
            };

            let mut clipped_in = false;
            for item in items.iter().rev() {
                if !item.rect.contains(&point_in_layer) ||
                    !item.clip_rect.contains(&point_in_layer) {
                    continue;
                }

                let clip_chain_index = clip_and_scroll.clip_chain_index;
                clipped_in = clipped_in ||
                    self.is_point_clipped_in_for_clip_chain(point, clip_chain_index, &mut test);
                if !clipped_in {
                    break;
                }

                // Don't hit items with backface-visibility:hidden if they are facing the back.
                if !item.is_backface_visible {
                    if *facing_backwards.get_or_insert_with(|| transform.is_backface_visible()) {
                        continue;
                    }
                }

                // We need to calculate the position of the test point relative to the origin of
                // the pipeline of the hit item. If we cannot get a transformed point, we are
                // in a situation with an uninvertible transformation so we should just skip this
                // result.
                let root_node = &self.spatial_nodes[self.pipeline_root_nodes[&pipeline_id].0];
                let point_in_viewport = match root_node.world_viewport_transform.inverse() {
                    Some(inverted) => inverted.transform_point2d(&point),
                    None => continue,
                };

                result.items.push(HitTestItem {
                    pipeline: pipeline_id,
                    tag: item.tag,
                    point_in_viewport,
                    point_relative_to_item: point_in_layer - item.rect.origin.to_vector(),
                });
                if !test.flags.contains(HitTestFlags::FIND_ALL) {
                    return result;
                }
            }
        }

        result.items.dedup();
        result
    }

    pub fn get_pipeline_root(&self, pipeline_id: PipelineId) -> &HitTestSpatialNode {
        &self.spatial_nodes[self.pipeline_root_nodes[&pipeline_id].0]
    }
}

fn get_regions_for_clip_node(
    node: &ClipNode,
    clip_store: &ClipStore
) -> Vec<HitTestRegion> {
    let handle = match node.handle.as_ref() {
        Some(handle) => handle,
        None => {
            warn!("Encountered an empty clip node unexpectedly.");
            return Vec::new()
        }
    };

    let clips = clip_store.get(handle).clips();
    clips.iter().map(|source| {
        match source.0 {
            ClipSource::Rectangle(ref rect, mode) => HitTestRegion::Rectangle(*rect, mode),
            ClipSource::RoundedRectangle(ref rect, ref radii, ref mode) =>
                HitTestRegion::RoundedRectangle(*rect, *radii, *mode),
            ClipSource::Image(ref mask) => HitTestRegion::Rectangle(mask.rect, ClipMode::Clip),
            ClipSource::LineDecoration(_) |
            ClipSource::BoxShadow(_) => {
                unreachable!("Didn't expect to hit test against BorderCorner / BoxShadow / LineDecoration");
            }
        }
    }).collect()
}

#[derive(Clone, Copy, PartialEq)]
enum ClippedIn {
    ClippedIn,
    NotClippedIn,
}

pub struct HitTest {
    pipeline_id: Option<PipelineId>,
    point: WorldPoint,
    flags: HitTestFlags,
    node_cache: FastHashMap<ClipNodeIndex, ClippedIn>,
    clip_chain_cache: Vec<Option<ClippedIn>>,
}

impl HitTest {
    pub fn new(
        pipeline_id: Option<PipelineId>,
        point: WorldPoint,
        flags: HitTestFlags,
    ) -> HitTest {
        HitTest {
            pipeline_id,
            point,
            flags,
            node_cache: FastHashMap::default(),
            clip_chain_cache: Vec::new(),
        }
    }

    fn get_from_clip_chain_cache(&mut self, index: ClipChainIndex) -> Option<ClippedIn> {
        if index.0 >= self.clip_chain_cache.len() {
            None
        } else {
            self.clip_chain_cache[index.0]
        }
    }

    fn set_in_clip_chain_cache(&mut self, index: ClipChainIndex, value: ClippedIn) {
        if index.0 >= self.clip_chain_cache.len() {
            self.clip_chain_cache.resize(index.0 + 1, None);
        }
        self.clip_chain_cache[index.0] = Some(value);
    }

    fn get_absolute_point(&self, hit_tester: &HitTester) -> WorldPoint {
        if !self.flags.contains(HitTestFlags::POINT_RELATIVE_TO_PIPELINE_VIEWPORT) {
            return self.point;
        }

        let point =  &LayoutPoint::new(self.point.x, self.point.y);
        self.pipeline_id.map(|id|
            hit_tester.get_pipeline_root(id).world_viewport_transform.transform_point2d(point)
        ).unwrap_or_else(|| WorldPoint::new(self.point.x, self.point.y))
    }
}
<|endoftext|>


<commit_before>// Abstraction of the database.
// For the purposes of the backend, the only necessary
// actions are the following:
//
// 1. Put a pending build into the database.
// 2. Grab a pending build from the database, which
//    will immediately undergo being built.
// 3. Put test results into a database, given what
//    build was pending

extern crate postgres;
#[phase(plugin)]
extern crate pg_typeprovider;

use self::postgres::{Connection, SslMode};

use builder::BuildResult;

use self::EntryStatus::{Pending, InProgress, Done};

pg_table!(builds)

pub trait DatabaseEntry<A> : Send {
    fn get_base(&self) -> A;
}

impl<A : Send + Clone> DatabaseEntry<A> for A {
    fn get_base(&self) -> A { self.clone() }
}

/// Type A is some key
pub trait Database<A, B : DatabaseEntry<A>> : Sync + Send {
    fn add_pending(&self, entry: A);

    /// Optionally gets a pending build from the database.
    /// If `Some` is returned, it will not be returned again.
    /// If `None` is returned, it is expected that the caller will sleep.
    fn get_pending(&self) -> Option<B>;

    fn add_test_results(&self, entry: B, results: BuildResult);

    fn results_for_entry(&self, entry: &B) -> Option<String>;
}

pub enum EntryStatus {
    Pending,
    InProgress,
    Done
}

impl EntryStatus {
    pub fn to_int(&self) -> i32 {
        match *self {
            Pending => 0,
            InProgress => 1,
            Done => 2
        }
    }
}

pub mod postgres_db {
    use std::sync::Mutex;

    use super::postgres::{Connection, SslMode};

    use builder::BuildResult;
    use super::EntryStatus::{Pending, InProgress, Done};
    use super::{Database, DatabaseEntry, BuildInsert, Build};

    pub struct PostgresDatabase {
        db: Mutex<Connection>
    }

    impl PostgresDatabase {
        pub fn new(loc: &str) -> Option<PostgresDatabase> {
            Connection::connect(loc, &SslMode::None).ok().map(|db| {
                PostgresDatabase {
                    db: Mutex::new(db)
                }
            })
        }

        pub fn new_testing() -> Option<PostgresDatabase> {
            match PostgresDatabase::new(
                "postgres://jroesch@localhost/gradr-testing") {
                Some(db) => {
                    let lock = db.db.lock();
                    lock.execute(
                        "DROP TABLE IF EXISTS users", &[]);
                    lock.execute(
                        "DROP TABLE IF EXISTS builds", &[]);
                    lock.execute(
                        "CREATE TABLE users (
                            id SERIAL,
                            email varchar(500),
                            first_name varchar(500),
                            last_name varchar(500),
                            access_token varchar(500),
                            created_at timestamp without time zone,
                            updated_at timestamp without time zone,
                            github_username varchar(500),
                            password_digest varchar(500)
                            )");
                    lock.execute(
                        "CREATE TABLE builds (
                            id SERIAL,
                            status int,
                            clone_url text,
                            branch text,
                            results text
                            )");
                    Some(db)
                },
                None => None
            }
        }
    }

    impl DatabaseEntry<BuildInsert> for Build {
        fn get_base(&self) -> BuildInsert {
            BuildInsert {
                status: self.status,
                clone_url: self.clone_url.clone(),
                branch: self.branch.clone(),
                results: self.results.clone()
            }
        }
    }

    fn get_one_build(conn: &Connection) -> Option<Build> {
        let stmt = conn.prepare(
            "SELECT id, status, clone_url, branch, results FROM builds WHERE status=$1 LIMIT 1").unwrap();
        for row in stmt.query(&[&(&Pending).to_int()]).unwrap() {
            return Some(Build {
                id: row.get(0),
                status: row.get(1),
                clone_url: row.get(2),
                branch: row.get(3),
                results: row.get(4)
            })
        }

        None
    }

    // returns true if it was able to lock it, else false
    fn try_lock_build(conn: &Connection, b: &Build) -> bool {
        conn.execute(
            "UPDATE builds SET status=$1 WHERE id=$2 AND status=$3",
            &[&(&InProgress).to_int(), &b.id, &(&Pending).to_int()])
            .unwrap() == 1
    }

    impl Database<BuildInsert, Build> for PostgresDatabase {
        fn add_pending(&self, entry: BuildInsert) {
            entry.insert(&*self.db.lock());
        }

        fn get_pending(&self) -> Option<Build> {
            let conn: &Connection = &*self.db.lock();
            loop {
                match get_one_build(conn) {
                    Some(b) => {
                        if try_lock_build(conn, &b) {
                            return Some(b);
                        }
                    },
                    None => { return None; }
                }
            }
        }

        fn add_test_results(&self, entry: Build, results: BuildResult) {
            self.db.lock().execute(
                "UPDATE builds SET status=$1, results=$2 WHERE id=$3",
                &[&(&Done).to_int(), &results.to_string(), &entry.id]);
        }

        fn results_for_entry(&self, entry: &Build) -> Option<String> {
            let lock = self.db.lock();
            let stmt = lock.prepare(
                "SELECT results FROM builds WHERE id=$1 AND status=$2").unwrap();
            for row in stmt.query(&[&entry.id, &(&Done).to_int()]).unwrap() {
                return Some(row.get(0));
            }
            None
        }
    }
}

/// For integration tests.
pub mod testing {
    use std::collections::HashMap;
    use std::hash::Hash;
    use std::sync::mpmc_bounded_queue::Queue;
    use std::sync::RWLock;

    use builder::BuildResult;
    use super::{Database, DatabaseEntry};

    /// Simply a directory to a status.
    pub struct TestDatabase<A> {
        pending: Queue<A>,
        results: RWLock<HashMap<A, BuildResult>>,
    }

    impl<A : Clone + Eq + Send + Hash> TestDatabase<A> {
        pub fn new<A : Eq + Send + Hash>() -> TestDatabase<A> {
            TestDatabase {
                pending: Queue::with_capacity(10),
                results: RWLock::new(HashMap::new())
            }
        }
    }

    impl<A : Clone + Eq + Send + Hash> Database<A, A> for TestDatabase<A> {
        fn add_pending(&self, entry: A) {
            self.pending.push(entry);
        }

        fn get_pending(&self) -> Option<A> {
            self.pending.pop()
        }

        fn add_test_results(&self, entry: A, results: BuildResult) {
            let mut val = self.results.write();
            val.insert(entry, results);
            val.downgrade();
        }

        fn results_for_entry(&self, entry: &A) -> Option<String> {
            self.results.read().get(entry).map(|res| res.to_string())
        }
    }
}

#[cfg(test)]
pub mod tests {
    use super::Database;
    use super::testing::TestDatabase;

    use builder::BuildResult::TestSuccess;

    use std::collections::HashMap;

    static KEY : &'static str = "foobar";

    fn add_get_pending<D : Database<Path, Path>>(db: &D) {
        db.add_pending(Path::new(KEY));
        let actual = db.get_pending().and_then(|pending| {
            pending.as_str().map(|s| s.to_string())
        });
        let expected = Path::new(KEY).as_str().map(|s| s.to_string());
        assert_eq!(actual, expected);
    }

    fn add_test_results<D : Database<Path, Path>>(db: &D) {
        add_get_pending(db);
        db.add_test_results(Path::new(KEY),
                            TestSuccess(HashMap::new()));
    }

    #[test]
    fn memory_add_get_pending() {
        add_get_pending(&TestDatabase::<Path>::new());
    }

    #[test]
    fn memory_add_test_results() {
        add_test_results(&TestDatabase::<Path>::new());
    }
}
<commit_msg>Now uses a testing database for tests, which is automatically cleaned out between test runs.<commit_after>// Abstraction of the database.
// For the purposes of the backend, the only necessary
// actions are the following:
//
// 1. Put a pending build into the database.
// 2. Grab a pending build from the database, which
//    will immediately undergo being built.
// 3. Put test results into a database, given what
//    build was pending

extern crate postgres;
#[phase(plugin)]
extern crate pg_typeprovider;

use self::postgres::{Connection, SslMode};

use builder::BuildResult;

use self::EntryStatus::{Pending, InProgress, Done};

pg_table!(builds)

pub trait DatabaseEntry<A> : Send {
    fn get_base(&self) -> A;
}

impl<A : Send + Clone> DatabaseEntry<A> for A {
    fn get_base(&self) -> A { self.clone() }
}

/// Type A is some key
pub trait Database<A, B : DatabaseEntry<A>> : Sync + Send {
    fn add_pending(&self, entry: A);

    /// Optionally gets a pending build from the database.
    /// If `Some` is returned, it will not be returned again.
    /// If `None` is returned, it is expected that the caller will sleep.
    fn get_pending(&self) -> Option<B>;

    fn add_test_results(&self, entry: B, results: BuildResult);

    fn results_for_entry(&self, entry: &B) -> Option<String>;
}

pub enum EntryStatus {
    Pending,
    InProgress,
    Done
}

impl EntryStatus {
    pub fn to_int(&self) -> i32 {
        match *self {
            Pending => 0,
            InProgress => 1,
            Done => 2
        }
    }
}

pub mod postgres_db {
    use std::sync::Mutex;

    use super::postgres::{Connection, SslMode};

    use builder::BuildResult;
    use super::EntryStatus::{Pending, InProgress, Done};
    use super::{Database, DatabaseEntry, BuildInsert, Build};

    pub struct PostgresDatabase {
        db: Mutex<Connection>
    }

    impl PostgresDatabase {
        pub fn new(loc: &str) -> Option<PostgresDatabase> {
            Connection::connect(loc, &SslMode::None).ok().map(|db| {
                PostgresDatabase {
                    db: Mutex::new(db)
                }
            })
        }

        pub fn new_testing() -> Option<PostgresDatabase> {
            let retval = PostgresDatabase::new(
                "postgres://jroesch@localhost/gradr-testing");
            match retval {
                Some(ref db) => {
                    let lock = db.db.lock();
                    lock.execute(
                        "DROP TABLE IF EXISTS users", &[]);
                    lock.execute(
                        "DROP TABLE IF EXISTS builds", &[]);
                    lock.execute(
                        "CREATE TABLE users (
                            id SERIAL,
                            email varchar(500),
                            first_name varchar(500),
                            last_name varchar(500),
                            access_token varchar(500),
                            created_at timestamp without time zone,
                            updated_at timestamp without time zone,
                            github_username varchar(500),
                            password_digest varchar(500)
                            )", &[]);
                    lock.execute(
                        "CREATE TABLE builds (
                            id SERIAL,
                            status int,
                            clone_url text,
                            branch text,
                            results text
                            )", &[]);
                    },
                None => ()
            };
            retval
        }
    }

    impl DatabaseEntry<BuildInsert> for Build {
        fn get_base(&self) -> BuildInsert {
            BuildInsert {
                status: self.status,
                clone_url: self.clone_url.clone(),
                branch: self.branch.clone(),
                results: self.results.clone()
            }
        }
    }

    fn get_one_build(conn: &Connection) -> Option<Build> {
        let stmt = conn.prepare(
            "SELECT id, status, clone_url, branch, results FROM builds WHERE status=$1 LIMIT 1").unwrap();
        for row in stmt.query(&[&(&Pending).to_int()]).unwrap() {
            return Some(Build {
                id: row.get(0),
                status: row.get(1),
                clone_url: row.get(2),
                branch: row.get(3),
                results: row.get(4)
            })
        }

        None
    }

    // returns true if it was able to lock it, else false
    fn try_lock_build(conn: &Connection, b: &Build) -> bool {
        conn.execute(
            "UPDATE builds SET status=$1 WHERE id=$2 AND status=$3",
            &[&(&InProgress).to_int(), &b.id, &(&Pending).to_int()])
            .unwrap() == 1
    }

    impl Database<BuildInsert, Build> for PostgresDatabase {
        fn add_pending(&self, entry: BuildInsert) {
            entry.insert(&*self.db.lock());
        }

        fn get_pending(&self) -> Option<Build> {
            let conn: &Connection = &*self.db.lock();
            loop {
                match get_one_build(conn) {
                    Some(b) => {
                        if try_lock_build(conn, &b) {
                            return Some(b);
                        }
                    },
                    None => { return None; }
                }
            }
        }

        fn add_test_results(&self, entry: Build, results: BuildResult) {
            self.db.lock().execute(
                "UPDATE builds SET status=$1, results=$2 WHERE id=$3",
                &[&(&Done).to_int(), &results.to_string(), &entry.id]);
        }

        fn results_for_entry(&self, entry: &Build) -> Option<String> {
            let lock = self.db.lock();
            let stmt = lock.prepare(
                "SELECT results FROM builds WHERE id=$1 AND status=$2").unwrap();
            for row in stmt.query(&[&entry.id, &(&Done).to_int()]).unwrap() {
                return Some(row.get(0));
            }
            None
        }
    }
}

/// For integration tests.
pub mod testing {
    use std::collections::HashMap;
    use std::hash::Hash;
    use std::sync::mpmc_bounded_queue::Queue;
    use std::sync::RWLock;

    use builder::BuildResult;
    use super::{Database, DatabaseEntry};

    /// Simply a directory to a status.
    pub struct TestDatabase<A> {
        pending: Queue<A>,
        results: RWLock<HashMap<A, BuildResult>>,
    }

    impl<A : Clone + Eq + Send + Hash> TestDatabase<A> {
        pub fn new<A : Eq + Send + Hash>() -> TestDatabase<A> {
            TestDatabase {
                pending: Queue::with_capacity(10),
                results: RWLock::new(HashMap::new())
            }
        }
    }

    impl<A : Clone + Eq + Send + Hash> Database<A, A> for TestDatabase<A> {
        fn add_pending(&self, entry: A) {
            self.pending.push(entry);
        }

        fn get_pending(&self) -> Option<A> {
            self.pending.pop()
        }

        fn add_test_results(&self, entry: A, results: BuildResult) {
            let mut val = self.results.write();
            val.insert(entry, results);
            val.downgrade();
        }

        fn results_for_entry(&self, entry: &A) -> Option<String> {
            self.results.read().get(entry).map(|res| res.to_string())
        }
    }
}

#[cfg(test)]
pub mod tests {
    use super::Database;
    use super::testing::TestDatabase;

    use builder::BuildResult::TestSuccess;

    use std::collections::HashMap;

    static KEY : &'static str = "foobar";

    fn add_get_pending<D : Database<Path, Path>>(db: &D) {
        db.add_pending(Path::new(KEY));
        let actual = db.get_pending().and_then(|pending| {
            pending.as_str().map(|s| s.to_string())
        });
        let expected = Path::new(KEY).as_str().map(|s| s.to_string());
        assert_eq!(actual, expected);
    }

    fn add_test_results<D : Database<Path, Path>>(db: &D) {
        add_get_pending(db);
        db.add_test_results(Path::new(KEY),
                            TestSuccess(HashMap::new()));
    }

    #[test]
    fn memory_add_get_pending() {
        add_get_pending(&TestDatabase::<Path>::new());
    }

    #[test]
    fn memory_add_test_results() {
        add_test_results(&TestDatabase::<Path>::new());
    }
}
<|endoftext|>


<commit_before>use std::collections::{
    HashMap,
    BinaryHeap,
};
use std::collections::hash_map::Entry::{
    Occupied,
    Vacant,
};

use std::default::Default;
use std::hash::Hash;
use std::ops::{
    Add,
};

use scored::MinScored;
use super::visit::{
    Visitable,
    VisitMap,
};

/// [Generic] Dijkstra's shortest path algorithm.
///
/// Compute the length of the shortest path from `start` to every reachable
/// node.
///
/// The graph should be `Visitable`, and `edges` a closure that maps
/// a node identifier to an iterator of `(n, k)` pairs where `n` is an adjacent
/// node and `k` the edge weight.
///
/// If `goal` is not `None`, then the algorithm terminates once the `goal` node's
/// cost is calculated.
///
/// Returns a `HashMap` that maps `NodeId` to path cost.
pub fn dijkstra<'a, G: Visitable, K, F, Edges>(graph: &'a G,
                                               start: G::NodeId,
                                               goal: Option<G::NodeId>,
                                               mut edges: F) -> HashMap<G::NodeId, K> where
    G::NodeId: Eq + Hash,
    K: Default + Add<Output=K> + Copy + PartialOrd,
    F: FnMut(&'a G, G::NodeId) -> Edges,
    Edges: Iterator<Item=(G::NodeId, K)>,
{
    let mut visited = graph.visit_map();
    let mut scores = HashMap::new();
    //let mut predecessor = HashMap::new();
    let mut visit_next = BinaryHeap::new();
    let zero_score: K = Default::default();
    scores.insert(start.clone(), zero_score);
    visit_next.push(MinScored(zero_score, start));
    while let Some(MinScored(node_score, node)) = visit_next.pop() {
        if visited.is_visited(&node) {
            continue
        }
        if goal.as_ref() == Some(&node) {
            break
        }
        for (next, edge) in edges(graph, node.clone()) {
            if visited.is_visited(&next) {
                continue
            }
            let mut next_score = node_score + edge;
            match scores.entry(next.clone()) {
                Occupied(ent) => if next_score < *ent.get() {
                    *ent.into_mut() = next_score;
                    //predecessor.insert(next.clone(), node.clone());
                } else {
                    next_score = *ent.get();
                },
                Vacant(ent) => {
                    ent.insert(next_score);
                    //predecessor.insert(next.clone(), node.clone());
                }
            }
            visit_next.push(MinScored(next_score, next));
        }
        visited.visit(node);
    }
    scores
}
<commit_msg>Use GraphRef in dijkstra<commit_after>use std::collections::{
    HashMap,
    BinaryHeap,
};
use std::collections::hash_map::Entry::{
    Occupied,
    Vacant,
};

use std::default::Default;
use std::hash::Hash;
use std::ops::{
    Add,
};

use scored::MinScored;
use super::visit::{
    GraphRef,
    Visitable,
    VisitMap,
};

/// [Generic] Dijkstra's shortest path algorithm.
///
/// Compute the length of the shortest path from `start` to every reachable
/// node.
///
/// The graph should be `Visitable`, and `edges` a closure that maps
/// a node identifier to an iterator of `(n, k)` pairs where `n` is an adjacent
/// node and `k` the edge weight.
///
/// If `goal` is not `None`, then the algorithm terminates once the `goal` node's
/// cost is calculated.
///
/// Returns a `HashMap` that maps `NodeId` to path cost.
pub fn dijkstra<G, K, F, Edges>(graph: G,
                                start: G::NodeId,
                                goal: Option<G::NodeId>,
                                mut edges: F) -> HashMap<G::NodeId, K>
    where G: GraphRef + Visitable,
          G::NodeId: Eq + Hash,
          K: Default + Add<Output=K> + Copy + PartialOrd,
          F: FnMut(G, G::NodeId) -> Edges,
          Edges: Iterator<Item=(G::NodeId, K)>,
{
    let mut visited = graph.visit_map();
    let mut scores = HashMap::new();
    //let mut predecessor = HashMap::new();
    let mut visit_next = BinaryHeap::new();
    let zero_score: K = Default::default();
    scores.insert(start.clone(), zero_score);
    visit_next.push(MinScored(zero_score, start));
    while let Some(MinScored(node_score, node)) = visit_next.pop() {
        if visited.is_visited(&node) {
            continue
        }
        if goal.as_ref() == Some(&node) {
            break
        }
        for (next, edge) in edges(graph, node.clone()) {
            if visited.is_visited(&next) {
                continue
            }
            let mut next_score = node_score + edge;
            match scores.entry(next.clone()) {
                Occupied(ent) => if next_score < *ent.get() {
                    *ent.into_mut() = next_score;
                    //predecessor.insert(next.clone(), node.clone());
                } else {
                    next_score = *ent.get();
                },
                Vacant(ent) => {
                    ent.insert(next_score);
                    //predecessor.insert(next.clone(), node.clone());
                }
            }
            visit_next.push(MinScored(next_score, next));
        }
        visited.visit(node);
    }
    scores
}
<|endoftext|>


<commit_before>pub fn lt_encode(source: Vec<u8>, blocksize: u32)
<commit_msg>deleted empty file<commit_after><|endoftext|>


<commit_before>use std::collections::HashMap;
use error::EvaluatorError;

pub type Functions = HashMap<&'static str, Function>;
pub type Operation = (i32, String);
pub type FunctionHandle = Box<Fn(Vec<i32>) -> Result<Operation, EvaluatorError>>;

pub struct Function {
    pub arity: usize,
    pub handle: FunctionHandle,
}

impl Function {
    pub fn new(a: usize, h: FunctionHandle) -> Function {
        Function {
            arity: a,
            handle: h,
        }
    }
}

pub mod functions {
    pub type Operation = (i32, String);
    use error::EvaluatorError;

    // pub fn minus(mut args: Vec<i32>) -> Result<i32, EvaluatorError> {
    //     Ok(-args.pop().unwrap())
    // }

    pub fn not(mut args: Vec<i32>) -> Result<Operation, EvaluatorError> {
        let mut a: i32 = args.pop().unwrap();
        Ok((!a, format!("{}{}", "~".to_string(), a.to_string())))
    }

    pub fn and(mut args: Vec<i32>) -> Result<Operation, EvaluatorError> {
        let mut b = args.pop().unwrap();
        let mut a = args.pop().unwrap();
        Ok((a & b, format!("{} {} {}", a.to_string(), "&".to_string(), b.to_string())))
    }

    pub fn or(mut args: Vec<i32>) -> Result<Operation, EvaluatorError> {
        let mut b = args.pop().unwrap();
        let mut a = args.pop().unwrap();
        Ok((a | b, format!("{} {} {}", a.to_string(), "|".to_string(), b.to_string())))
    }

    pub fn xor(mut args: Vec<i32>) -> Result<Operation, EvaluatorError> {
        let mut b = args.pop().unwrap();
        let mut a = args.pop().unwrap();
        Ok((a ^ b, format!("{} {} {}", a.to_string(), "^".to_string(), b.to_string())))
    }

    pub fn rshift(mut args: Vec<i32>) -> Result<Operation, EvaluatorError> {
        let mut b = args.pop().unwrap();
        let mut a = args.pop().unwrap();
        Ok((a >> b, format!("{} {} {}", a.to_string(), ">>".to_string(), b.to_string())))
    }

    pub fn lshift(mut args: Vec<i32>) -> Result<Operation, EvaluatorError> {
        let mut b = args.pop().unwrap();
        let mut a = args.pop().unwrap();
        Ok((a << b, format!("{} {} {}", a.to_string(), "<<".to_string(), b.to_string())))
    }

}
<commit_msg>Change Operation type to private<commit_after>use std::collections::HashMap;
use error::EvaluatorError;

type Operation = (i32, String);
pub type Functions = HashMap<&'static str, Function>;
pub type FunctionHandle = Box<Fn(Vec<i32>) -> Result<Operation, EvaluatorError>>;

pub struct Function {
    pub arity: usize,
    pub handle: FunctionHandle,
}

impl Function {
    pub fn new(a: usize, h: FunctionHandle) -> Function {
        Function {
            arity: a,
            handle: h,
        }
    }
}

pub mod functions {
    use error::EvaluatorError;
    type Operation = (i32, String);

    pub fn not(mut args: Vec<i32>) -> Result<Operation, EvaluatorError> {
        let mut a: i32 = args.pop().unwrap();
        Ok((!a, format!("{}{}", "~".to_string(), a.to_string())))
    }

    pub fn and(mut args: Vec<i32>) -> Result<Operation, EvaluatorError> {
        let mut b = args.pop().unwrap();
        let mut a = args.pop().unwrap();
        Ok((a & b, format!("{} {} {}", a.to_string(), "&".to_string(), b.to_string())))
    }

    pub fn or(mut args: Vec<i32>) -> Result<Operation, EvaluatorError> {
        let mut b = args.pop().unwrap();
        let mut a = args.pop().unwrap();
        Ok((a | b, format!("{} {} {}", a.to_string(), "|".to_string(), b.to_string())))
    }

    pub fn xor(mut args: Vec<i32>) -> Result<Operation, EvaluatorError> {
        let mut b = args.pop().unwrap();
        let mut a = args.pop().unwrap();
        Ok((a ^ b, format!("{} {} {}", a.to_string(), "^".to_string(), b.to_string())))
    }

    pub fn rshift(mut args: Vec<i32>) -> Result<Operation, EvaluatorError> {
        let mut b = args.pop().unwrap();
        let mut a = args.pop().unwrap();
        Ok((a >> b, format!("{} {} {}", a.to_string(), ">>".to_string(), b.to_string())))
    }

    pub fn lshift(mut args: Vec<i32>) -> Result<Operation, EvaluatorError> {
        let mut b = args.pop().unwrap();
        let mut a = args.pop().unwrap();
        Ok((a << b, format!("{} {} {}", a.to_string(), "<<".to_string(), b.to_string())))
    }

}
<|endoftext|>


<commit_before>use crate::fastfield::serializer::FastSingleFieldSerializer;
use crate::fastfield::value_to_u64;
use crate::fastfield::FastFieldSerializer;
use crate::postings::UnorderedTermId;
use crate::schema::{Document, Field};
use crate::termdict::TermOrdinal;
use crate::DocId;
use fnv::FnvHashMap;
use std::iter::once;
use std::io;

/// Writer for multi-valued (as in, more than one value per document)
/// int fast field.
///
/// This `Writer` is only useful for advanced user.
/// The normal way to get your multivalued int in your index
/// is to
/// - declare your field with fast set to `Cardinality::MultiValues`
/// in your schema
/// - add your document simply by calling `.add_document(...)`.
///
/// The `MultiValuedFastFieldWriter` can be acquired from the
/// fastfield writer, by calling [`.get_multivalue_writer(...)`](./struct.FastFieldsWriter.html#method.get_multivalue_writer).
///
/// Once acquired, writing is done by calling calls to
/// `.add_document_vals(&[u64])` once per document.
///
/// The serializer makes it possible to remap all of the values
/// that were pushed to the writer using a mapping.
/// This makes it possible to push unordered term ids,
/// during indexing and remap them to their respective
/// term ids when the segment is getting serialized.
pub struct MultiValuedFastFieldWriter {
    field: Field,
    vals: Vec<UnorderedTermId>,
    doc_index: Vec<u64>,
    is_facet: bool,
}

impl MultiValuedFastFieldWriter {
    /// Creates a new `IntFastFieldWriter`
    pub(crate) fn new(field: Field, is_facet: bool) -> Self {
        MultiValuedFastFieldWriter {
            field,
            vals: Vec::new(),
            doc_index: Vec::new(),
            is_facet,
        }
    }

    /// Access the field associated to the `MultiValuedFastFieldWriter`
    pub fn field(&self) -> Field {
        self.field
    }

    /// Finalize the current document.
    pub(crate) fn next_doc(&mut self) {
        self.doc_index.push(self.vals.len() as u64);
    }

    /// Pushes a new value to the current document.
    pub(crate) fn add_val(&mut self, val: UnorderedTermId) {
        self.vals.push(val);
    }

    /// Shift to the next document and adds
    /// all of the matching field values present in the document.
    pub fn add_document(&mut self, doc: &Document) {
        self.next_doc();
        // facets are indexed in the `SegmentWriter` as we encode their unordered id.
        if !self.is_facet {
            for field_value in doc.field_values() {
                if field_value.field() == self.field {
                    self.add_val(value_to_u64(field_value.value()));
                }
            }
        }
    }

    /// Register all of the values associated to a document.
    ///
    /// The method returns the `DocId` of the document that was
    /// just written.
    pub fn add_document_vals(&mut self, vals: &[UnorderedTermId]) -> DocId {
        let doc = self.doc_index.len() as DocId;
        self.next_doc();
        self.vals.extend_from_slice(vals);
        doc
    }

    /// Serializes fast field values by pushing them to the `FastFieldSerializer`.
    ///
    /// If a mapping is given, the values are remapped *and sorted* before serialization.
    /// This is used when serializing `facets`. Specifically their terms are
    /// first stored in the writer as their position in the `IndexWriter`'s `HashMap`.
    /// This value is called an `UnorderedTermId`.
    ///
    /// During the serialization of the segment, terms gets sorted and
    /// `tantivy` builds a mapping to convert this `UnorderedTermId` into
    /// term ordinals.
    ///
    pub fn serialize(
        &self,
        serializer: &mut FastFieldSerializer,
        mapping_opt: Option<&FnvHashMap<UnorderedTermId, TermOrdinal>>,
    ) -> io::Result<()> {
        {
            // writing the offset index
            let mut doc_index_serializer =
                serializer.new_u64_fast_field_with_idx(self.field, 0, self.vals.len() as u64, 0)?;
            for &offset in &self.doc_index {
                doc_index_serializer.add_val(offset)?;
            }
            doc_index_serializer.add_val(self.vals.len() as u64)?;
            doc_index_serializer.close_field()?;
        }
        {
            // writing the values themselves.
            let mut value_serializer: FastSingleFieldSerializer<'_, _>;
            match mapping_opt {
                Some(mapping) => {
                    value_serializer = serializer.new_u64_fast_field_with_idx(
                        self.field,
                        0u64,
                        mapping.len() as u64,
                        1,
                    )?;

                    let last_interval =
                        self.doc_index.last().cloned().unwrap() as usize..self.vals.len();

                    let mut doc_vals: Vec<u64> = Vec::with_capacity(100);
                    for range in self
                        .doc_index
                        .windows(2)
                        .map(|interval| interval[0] as usize..interval[1] as usize)
                        .chain(once(last_interval))
                    {
                        doc_vals.clear();
                        let remapped_vals = self.vals[range]
                            .iter()
                            .map(|val| *mapping.get(val).expect("Missing term ordinal"));
                        doc_vals.extend(remapped_vals);
                        doc_vals.sort_unstable();
                        for &val in &doc_vals {
                            value_serializer.add_val(val)?;
                        }
                    }
                }
                None => {
                    let val_min_max = crate::common::minmax(self.vals.iter().cloned());
                    let (val_min, val_max) = val_min_max.unwrap_or((0u64, 0u64));
                    value_serializer =
                        serializer.new_u64_fast_field_with_idx(self.field, val_min, val_max, 1)?;
                    for &val in &self.vals {
                        value_serializer.add_val(val)?;
                    }
                }
            }
            value_serializer.close_field()?;
        }
        Ok(())
    }
}
<commit_msg>Cargo fmt<commit_after>use crate::fastfield::serializer::FastSingleFieldSerializer;
use crate::fastfield::value_to_u64;
use crate::fastfield::FastFieldSerializer;
use crate::postings::UnorderedTermId;
use crate::schema::{Document, Field};
use crate::termdict::TermOrdinal;
use crate::DocId;
use fnv::FnvHashMap;
use std::io;
use std::iter::once;

/// Writer for multi-valued (as in, more than one value per document)
/// int fast field.
///
/// This `Writer` is only useful for advanced user.
/// The normal way to get your multivalued int in your index
/// is to
/// - declare your field with fast set to `Cardinality::MultiValues`
/// in your schema
/// - add your document simply by calling `.add_document(...)`.
///
/// The `MultiValuedFastFieldWriter` can be acquired from the
/// fastfield writer, by calling [`.get_multivalue_writer(...)`](./struct.FastFieldsWriter.html#method.get_multivalue_writer).
///
/// Once acquired, writing is done by calling calls to
/// `.add_document_vals(&[u64])` once per document.
///
/// The serializer makes it possible to remap all of the values
/// that were pushed to the writer using a mapping.
/// This makes it possible to push unordered term ids,
/// during indexing and remap them to their respective
/// term ids when the segment is getting serialized.
pub struct MultiValuedFastFieldWriter {
    field: Field,
    vals: Vec<UnorderedTermId>,
    doc_index: Vec<u64>,
    is_facet: bool,
}

impl MultiValuedFastFieldWriter {
    /// Creates a new `IntFastFieldWriter`
    pub(crate) fn new(field: Field, is_facet: bool) -> Self {
        MultiValuedFastFieldWriter {
            field,
            vals: Vec::new(),
            doc_index: Vec::new(),
            is_facet,
        }
    }

    /// Access the field associated to the `MultiValuedFastFieldWriter`
    pub fn field(&self) -> Field {
        self.field
    }

    /// Finalize the current document.
    pub(crate) fn next_doc(&mut self) {
        self.doc_index.push(self.vals.len() as u64);
    }

    /// Pushes a new value to the current document.
    pub(crate) fn add_val(&mut self, val: UnorderedTermId) {
        self.vals.push(val);
    }

    /// Shift to the next document and adds
    /// all of the matching field values present in the document.
    pub fn add_document(&mut self, doc: &Document) {
        self.next_doc();
        // facets are indexed in the `SegmentWriter` as we encode their unordered id.
        if !self.is_facet {
            for field_value in doc.field_values() {
                if field_value.field() == self.field {
                    self.add_val(value_to_u64(field_value.value()));
                }
            }
        }
    }

    /// Register all of the values associated to a document.
    ///
    /// The method returns the `DocId` of the document that was
    /// just written.
    pub fn add_document_vals(&mut self, vals: &[UnorderedTermId]) -> DocId {
        let doc = self.doc_index.len() as DocId;
        self.next_doc();
        self.vals.extend_from_slice(vals);
        doc
    }

    /// Serializes fast field values by pushing them to the `FastFieldSerializer`.
    ///
    /// If a mapping is given, the values are remapped *and sorted* before serialization.
    /// This is used when serializing `facets`. Specifically their terms are
    /// first stored in the writer as their position in the `IndexWriter`'s `HashMap`.
    /// This value is called an `UnorderedTermId`.
    ///
    /// During the serialization of the segment, terms gets sorted and
    /// `tantivy` builds a mapping to convert this `UnorderedTermId` into
    /// term ordinals.
    ///
    pub fn serialize(
        &self,
        serializer: &mut FastFieldSerializer,
        mapping_opt: Option<&FnvHashMap<UnorderedTermId, TermOrdinal>>,
    ) -> io::Result<()> {
        {
            // writing the offset index
            let mut doc_index_serializer =
                serializer.new_u64_fast_field_with_idx(self.field, 0, self.vals.len() as u64, 0)?;
            for &offset in &self.doc_index {
                doc_index_serializer.add_val(offset)?;
            }
            doc_index_serializer.add_val(self.vals.len() as u64)?;
            doc_index_serializer.close_field()?;
        }
        {
            // writing the values themselves.
            let mut value_serializer: FastSingleFieldSerializer<'_, _>;
            match mapping_opt {
                Some(mapping) => {
                    value_serializer = serializer.new_u64_fast_field_with_idx(
                        self.field,
                        0u64,
                        mapping.len() as u64,
                        1,
                    )?;

                    let last_interval =
                        self.doc_index.last().cloned().unwrap() as usize..self.vals.len();

                    let mut doc_vals: Vec<u64> = Vec::with_capacity(100);
                    for range in self
                        .doc_index
                        .windows(2)
                        .map(|interval| interval[0] as usize..interval[1] as usize)
                        .chain(once(last_interval))
                    {
                        doc_vals.clear();
                        let remapped_vals = self.vals[range]
                            .iter()
                            .map(|val| *mapping.get(val).expect("Missing term ordinal"));
                        doc_vals.extend(remapped_vals);
                        doc_vals.sort_unstable();
                        for &val in &doc_vals {
                            value_serializer.add_val(val)?;
                        }
                    }
                }
                None => {
                    let val_min_max = crate::common::minmax(self.vals.iter().cloned());
                    let (val_min, val_max) = val_min_max.unwrap_or((0u64, 0u64));
                    value_serializer =
                        serializer.new_u64_fast_field_with_idx(self.field, val_min, val_max, 1)?;
                    for &val in &self.vals {
                        value_serializer.add_val(val)?;
                    }
                }
            }
            value_serializer.close_field()?;
        }
        Ok(())
    }
}
<|endoftext|>