diff --git a/src/common/cpp_essentials/bitmatrix_cursor.rs b/src/common/cpp_essentials/bitmatrix_cursor.rs index 420eb2f..5cf6950 100644 --- a/src/common/cpp_essentials/bitmatrix_cursor.rs +++ b/src/common/cpp_essentials/bitmatrix_cursor.rs @@ -78,6 +78,7 @@ pub trait BitMatrixCursor { // } fn movedBy(self, d: Point) -> Self; + fn turnedBack(&self) -> Self;// { return {*img, p, back()}; } // { // auto res = *this; // res.p += d; @@ -160,6 +161,8 @@ pub trait BitMatrixCursor { res } + fn p(&self) -> Point; + // template // ARRAY readPattern(int range = 0) // { diff --git a/src/common/cpp_essentials/concentric_finder.rs b/src/common/cpp_essentials/concentric_finder.rs index 8b13789..6fffe69 100644 --- a/src/common/cpp_essentials/concentric_finder.rs +++ b/src/common/cpp_essentials/concentric_finder.rs @@ -1 +1,532 @@ +use crate::{ + common::{ + cpp_essentials::{ + Direction, FixedPattern, IsPattern, PatternRow, PatternType, PatternView, + }, + BitMatrix, Quadrilateral, + }, + point, Point, +}; +use super::{ + BitMatrixCursor, DMRegressionLine, EdgeTracer, FastEdgeToEdgeCounter, Pattern, RegressionLine, +}; + +pub fn CenterFromEnd + std::iter::Sum + Copy>( + pattern: &[T; N], + end: f32, +) -> f32 { + if (N == 5) { + let a: f32 = pattern[4].into() + pattern[3].into() + pattern[2].into() / 2.0; + let b: f32 = + pattern[4].into() + (pattern[3].into() + pattern[2].into() + pattern[1].into()) / 2.0; + let c: f32 = (pattern[4].into() + + pattern[3].into() + + pattern[2].into() + + pattern[1].into() + + pattern[0].into()) + / 2.0; + end - (2.0 * a + b + c) / 4.0 + } else if (N == 3) { + let a: f32 = pattern[2].into() + pattern[1].into() / 2.0; + let b: f32 = (pattern[2].into() + pattern[1].into() + pattern[0].into()) / 2.0; + end - (2.0 * a + b) / 3.0 + } else { + // aztec + let a: f32 = + pattern.iter().skip(N / 2 + 1).copied().sum::().into() + pattern[N / 2].into() / 2.0; + // let a = std::accumulate(pattern.begin() + (N/2 + 1), pattern.end(), pattern[N/2] / 2.0); + end - a + } +} + +pub fn ReadSymmetricPattern( + cur: &mut Cursor, + range: i32, +) -> Option> { + assert!(N % 2 == 1); + + assert!(range > 0); + + let mut range = range; + + let mut res: Pattern = [0; N]; + let s_2 = res.len() as isize / 2; + let mut cuo = cur.turnedBack(); + + let mut next = |cur: &mut Cursor, i: isize| { + let v = cur.stepToEdge(Some(1), Some(range), None); + res[(s_2 + i) as usize] = (res[(s_2 + i) as usize] as i32 + v) as u16; + // res[(s_2 + i) as usize] += v; + if (range != 0) { + range -= v; + } + + v + }; + + for i in 0..=s_2 { + // for (int i = 0; i <= s_2; ++i) { + if (!next(cur, i) != 0 || !next(&mut cuo, -i) != 0) { + return None; + } + } + res[s_2 as usize] -= 1; // the starting pixel has been counted twice, fix this + + Some(res) +} + +// default for RELAXED_THRESHOLD should be false +pub fn CheckSymmetricPattern< + const RELAXED_THRESHOLD: bool, + const LEN: usize, + const SUM: usize, + T: BitMatrixCursor, +>( + cur: &mut T, + pattern: &Pattern, + range: i32, + updatePosition: bool, +) -> i32 { + let mut range = range; + + let curFwd: FastEdgeToEdgeCounter = FastEdgeToEdgeCounter::new(cur); + let curBwd: FastEdgeToEdgeCounter = FastEdgeToEdgeCounter::new(&cur.turnedBack()); + + let centerFwd = curFwd.stepToNextEdge(range); + if (!(centerFwd != 0)) { + return 0; + } + let centerBwd = curBwd.stepToNextEdge(range); + if (!(centerBwd != 0)) { + return 0; + } + + assert!(range > 0); + let mut res: PatternRow = PatternRow::new(vec![0; LEN]); + let s_2 = (res.len()) / 2; + res[s_2] = (centerFwd + centerBwd - 1) as u16; // -1 because the starting pixel is counted twice + range -= res[s_2] as i32; + + let mut next = |cur: &FastEdgeToEdgeCounter, i: isize| { + let v = cur.stepToNextEdge(range); + res[(s_2 as isize + i) as usize] = v as u16; + range -= v; + + v + }; + + for i in 1..=s_2 { + // for (int i = 1; i <= s_2; ++i) { + if (!(next(&curFwd, i as isize) != 0) || !(next(&curBwd, -(i as isize)) != 0)) { + return 0; + } + } + + if (!(IsPattern( + &PatternView::new(&res), + &FixedPattern::::with_reference(pattern), + None, + 0.0, + 0.0, + Some(RELAXED_THRESHOLD), + ) != 0.0)) + { + return 0; + } + + if (updatePosition) { + cur.step(Some((res[s_2] as i32 / 2 - (centerBwd as i32 - 1)) as f32)); + } + + res.into_iter().sum::() as i32 +} + +pub fn AverageEdgePixels( + cur: &mut T, + range: i32, + numOfEdges: u32, +) -> Option { + let mut sum = Point::default(); + + for i in 0..numOfEdges { + // for (int i = 0; i < numOfEdges; ++i) { + if (!cur.isInSelf()) { + return None; + } + cur.stepToEdge(Some(1), Some(range), None); + sum += cur.p().centered() + (cur.p() + cur.back()).centered() + // sum += centered(cur.p) + centered(cur.p + cur.back()); + // log(cur.p + cur.back(), 2); + } + Some(sum / (2 * numOfEdges) as f32) +} + +pub fn CenterOfDoubleCross( + image: &BitMatrix, + center: Point, + range: i32, + numOfEdges: u32, +) -> Option { + let mut sum = Point::default(); + + for d in [ + point(0.0, 1.0), + point(1.0, 0.0), + point(1.0, 1.0), + point(1.0, -1.0), + ] { + // for (auto d : {PointI{0, 1}, {1, 0}, {1, 1}, {1, -1}}) { + let avr1 = AverageEdgePixels(&mut EdgeTracer::new(image, center, d), range, numOfEdges)?; + let avr2 = AverageEdgePixels(&mut EdgeTracer::new(image, center, -d), range, numOfEdges)?; + + sum += avr1 + avr2; + } + Some(sum / 8.0) +} + +pub fn CenterOfRing( + image: &BitMatrix, + center: Point, + range: i32, + nth: i32, + requireCircle: bool, +) -> Option { + // range is the approximate width/height of the nth ring, if nth>1 then it would be plausible to limit the search radius + // to approximately range / 2 * sqrt(2) == range * 0.75 but it turned out to be too limiting with realworld/noisy data. + let radius = range; + let inner = nth < 0; + let nth = nth.abs(); + // log(center, 3); + let mut cur = EdgeTracer::new(image, center, point(0.0, 1.0)); + if (!(cur.stepToEdge(Some(nth), Some(radius), Some(inner)) != 0)) { + return None; + } + cur.turnRight(); // move clock wise and keep edge on the right/left depending on backup + let edgeDir = if inner { + Direction::Left + } else { + Direction::Right + }; + + let mut neighbourMask = 0; + let start = cur.p(); + let mut sum = Point::default(); + let mut n = 0; + loop { + // log(cur.p, 4); + sum += cur.p().centered(); + n += 1; + + // find out if we come full circle around the center. 8 bits have to be set in the end. + neighbourMask |= (1 + << (4.0 + Point::dot(Point::bresenhamDirection(cur.p() - center), point(1.0, 3.0))) + as u32); + + if (!cur.stepAlongEdge(edgeDir, None)) { + return None; + } + + // use L-inf norm, simply because it is a lot faster than L2-norm and sufficiently accurate + if (Point::maxAbsComponent(cur.p - center) > radius as f32 + || center == cur.p + || n > 4 * 2 * range) + { + return None; + } + + if !(cur.p != start) { + break; + } + } //while (cur.p != start); + + if (requireCircle && neighbourMask != 0b111101111) { + return None; + } + + Some(sum / n as f32) +} + +pub fn CenterOfRings( + image: &BitMatrix, + center: Point, + range: i32, + numOfRings: u32, +) -> Option { + let mut n = numOfRings; + let mut sum = numOfRings * center; + for i in 1..numOfRings { + // for (int i = 1; i < numOfRings; ++i) { + let c = CenterOfRing(image, center, range, i as i32 + 1, false)?; + + // TODO: decide whether this wheighting depending on distance to the center is worth it + let weight = numOfRings - i; + sum += weight * c; + n += weight; + } + Some(sum / n as f32) +} + +pub fn FinetuneConcentricPatternCenter( + image: &BitMatrix, + center: Point, + range: i32, + finderPatternSize: u32, +) -> Option { + // make sure we have at least one path of white around the center + let res = CenterOfRing(image, center, range, 1, false)?; + + let center = res; + + let mut res = CenterOfRings(image, center, range, finderPatternSize / 2); + + if (res.is_none() || !image.get_point(res?)) { + res = CenterOfDoubleCross(image, (center), range, finderPatternSize / 2 + 1); + } + if (res.is_none() || !image.get_point(res?)) { + res = Some(center); + } + if (res.is_none() || !image.get_point(res?)) { + return None; + } + + res +} + +pub fn CollectRingPoints( + image: &BitMatrix, + center: Point, + range: i32, + edgeIndex: i32, + backup: bool, +) -> Vec { + let centerI = center.round(); + let radius = range; + let mut cur = EdgeTracer::new(image, centerI, point(0.0, 1.0)); + if (!(cur.stepToEdge(Some(edgeIndex), Some(radius), Some(backup)) != 0)) { + return Vec::default(); + } + cur.turnRight(); // move clock wise and keep edge on the right/left depending on backup + let edgeDir = if backup { + Direction::Left + } else { + Direction::Right + }; + + let mut neighbourMask = 0; + let start = cur.p(); + let mut points = Vec::::with_capacity(4 * range as usize); + + loop { + // log(cur.p, 4); + points.push((cur.p().centered())); + + // find out if we come full circle around the center. 8 bits have to be set in the end. + neighbourMask |= (1 + << (4.0 + Point::dot(Point::bresenhamDirection(cur.p - centerI), point(1.0, 3.0))) + as u32); + + if (!cur.stepAlongEdge(edgeDir, None)) { + return Vec::default(); + } + + // use L-inf norm, simply because it is a lot faster than L2-norm and sufficiently accurate + if (Point::maxAbsComponent(cur.p - centerI) > radius as f32 + || centerI == cur.p + || (points).len() > 4 * 2 * range as usize) + { + return Vec::default(); + } + + if !(cur.p != start) { + break; + } + } //while (cur.p != start); + + if (neighbourMask != 0b111101111) { + return Vec::default(); + } + + points +} + +pub fn FitQadrilateralToPoints(center: Point, points: &mut [Point]) -> Option { + let dist2Center = |a, b| Point::distance(a, center) < Point::distance(b, center); + // rotate points such that the first one is the furthest away from the center (hence, a corner) + + let max_by_pred = |a: &Point, b: &Point| { + if dist2Center(*a, *b) { + std::cmp::Ordering::Greater + } else { + std::cmp::Ordering::Less + } + }; + + let max = points.iter().copied().max_by(max_by_pred)?; + + let pos = points.iter().position(|e| *e == max)?; + + points.rotate_left(pos); + // std::rotate(points.begin(), std::max_element(points.begin(), points.end(), dist2Center), points.end()); + + let mut corners = [Point::default(); 4]; + corners[0] = points[0]; + // find the oposite corner by looking for the farthest point near the oposite point + points[(points.len() * 3 / 8)..=(points.len() * 5 / 8)] + .iter() + .copied() + .max_by(max_by_pred)?; + // corners[2] = std::max_element(&points[Size(points) * 3 / 8], &points[Size(points) * 5 / 8], dist2Center); + // find the two in between corners by looking for the points farthest from the long diagonal + let l = DMRegressionLine::with_two_points(corners[0], corners[2]); + let dist2Diagonal = /*[l = RegressionLine(*corners[0], *corners[2])]*/| a, b| { l.distance_single(a) < l.distance_single(b) }; + + let diagonal_max_by_pred = |p1: &Point, p2: &Point| { + if dist2Diagonal(*p1, *p2) { + std::cmp::Ordering::Greater + } else { + std::cmp::Ordering::Less + } + }; + corners[1] = points[(points.len() * 1 / 8)..=(points.len() * 3 / 8)] + .iter() + .copied() + .max_by(diagonal_max_by_pred)?; + // corners[1] = std::max_element(&points[Size(points) * 1 / 8], &points[Size(points) * 3 / 8], dist2Diagonal); + corners[3] = points[(points.len() * 5 / 8)..=(points.len() * 7 / 8)] + .iter() + .copied() + .max_by(diagonal_max_by_pred)?; + // corners[3] = std::max_element(&points[Size(points) * 5 / 8], &points[Size(points) * 7 / 8], dist2Diagonal); + + let lines = [ + DMRegressionLine::with_two_points((corners[0] + 1.0), corners[1]), + DMRegressionLine::with_two_points((corners[1] + 1.0), corners[2]), + DMRegressionLine::with_two_points((corners[2] + 1.0), corners[3]), + DMRegressionLine::with_two_points((corners[3] + 1.0), (*points.last()? + 1.0)), + ]; + // std::array lines{RegressionLine{corners[0] + 1, corners[1]}, RegressionLine{corners[1] + 1, corners[2]}, + // RegressionLine{corners[2] + 1, corners[3]}, RegressionLine{corners[3] + 1, &points.back() + 1}}; + if lines.iter().any(|line| !line.isValid()) { + return None; + } + + let mut res = Quadrilateral::default(); + for i in 0..4 { + // for (int i = 0; i < 4; ++i) { + res[i] = DMRegressionLine::intersect(&lines[i], &lines[(i + 1) % 4])?; + } + + Some(res) +} + +pub fn QuadrilateralIsPlausibleSquare(q: &Quadrilateral, lineIndex: usize) -> bool { + let mut m = f64::default(); + // let mut M = f64::default(); + + m = Point::distance(q[0], q[3]) as f64; //M = distance(q[0], q[3]); + let mut M = m; + + for i in 1..4 { + // for (int i = 1; i < 4; ++i) + + UpdateMinMaxFloat(&mut m, &mut M, Point::distance(q[i - 1], q[i]) as f64); + } + + m >= (lineIndex * 2) as f64 && m > M / 3.0 +} + +pub fn FitSquareToPoints( + image: &BitMatrix, + center: Point, + range: i32, + lineIndex: i32, + backup: bool, +) -> Option { + let mut points = CollectRingPoints(image, center, range, lineIndex, backup); + if (points.is_empty()) { + return None; + } + + let res = FitQadrilateralToPoints(center, &mut points)?; + if (!QuadrilateralIsPlausibleSquare(&res, (lineIndex - i32::from(backup)) as usize)) { + return None; + } + + Some(res) +} + +pub fn FindConcentricPatternCorners( + image: &BitMatrix, + center: Point, + range: i32, + lineIndex: i32, +) -> Option { + let innerCorners = FitSquareToPoints(image, center, range, lineIndex, false)?; + + let outerCorners = FitSquareToPoints(image, center, range, lineIndex + 1, true)?; + + let res = Quadrilateral::blend(&innerCorners, &outerCorners); + + // for p in innerCorners{ + // log(p, 3);} + + // for p in outerCorners{ + // log(p, 3);} + + // for p in res{ + // log(p, 3);} + + Some(res) +} + +#[derive(Default)] +pub struct ConcentricPattern { + p: Point, + size: usize, +} + + pub fn LocateConcentricPattern( image:&BitMatrix, pattern:&Pattern, center:Point, range:i32) -> Option +{ + let mut cur = EdgeTracer::new(image, center, Point::default()); + let mut minSpread = image.getWidth() as i32; + let mut maxSpread = 0_i32; + for d in [point(0.0,1.0), point(1.0,0.0)] { + // for (auto d : {PointI{0, 1}, {1, 0}}) { + cur.setDirection(d); // THIS COULD POSSIBLY BE WRONG, WE MIGHT MEAN TO CLONE cur EACH RUN? + let spread = CheckSymmetricPattern(&mut cur, pattern, range, true); + if (!(spread != 0)) + {return None} + UpdateMinMax(&mut minSpread, &mut maxSpread, spread); + } + +//#if 1 +for d in [point(1.0,1.0), point(1.0,-1.0)] { + // for (auto d : {PointI{1, 1}, {1, -1}}) { + cur.setDirection(d);// THIS COULD POSSIBLY BE WRONG, WE MIGHT MEAN TO CLONE cur EACH RUN? + let spread = CheckSymmetricPattern(&mut cur, pattern, range * 2, false); + if (!(spread != 0)) + {return None} + UpdateMinMax(&mut minSpread, &mut maxSpread, spread); + } +//#endif + + if (maxSpread > 5 * minSpread) + {return None} + + let newCenter = FinetuneConcentricPatternCenter(image, cur.p(), range, pattern.len() as u32)?; + + Some(ConcentricPattern{*newCenter, (maxSpread + minSpread) / 2}) +} + +fn UpdateMinMax(min: &mut T, max: &mut T, val: T) { + *min = std::cmp::min(*min, val); + *max = std::cmp::max(*max, val); +} + +fn UpdateMinMaxFloat(min: &mut f64, max: &mut f64, val: f64) { + *min = f64::min(*min, val); + *max = f64::max(*max, val); +} diff --git a/src/common/cpp_essentials/dm_regression_line.rs b/src/common/cpp_essentials/dm_regression_line.rs index d6e8d9e..8a574bd 100644 --- a/src/common/cpp_essentials/dm_regression_line.rs +++ b/src/common/cpp_essentials/dm_regression_line.rs @@ -1,5 +1,5 @@ use crate::common::Result; -use crate::{Exceptions, Point}; +use crate::{Exceptions, Point, point}; use super::{ util::{float_max, float_min}, @@ -211,9 +211,27 @@ impl RegressionLine for DMRegressionLine { Point::dot(self.direction_inward, self.normal()) > 0.5 // angle between original and new direction is at most 60 degree } + + fn a(&self) -> f32 { + self.a + } + + fn b(&self) -> f32 { + self.b + } + + fn c(&self) -> f32 { + self.c + } } impl DMRegressionLine { + pub fn with_two_points(point1: Point, point2: Point) -> Self { + let mut new_rl = DMRegressionLine::default(); + new_rl.evaluate(&[point1, point2]); + new_rl + } + // template fn average(c: &[f64], f: T) -> f64 where diff --git a/src/common/cpp_essentials/edge_tracer.rs b/src/common/cpp_essentials/edge_tracer.rs index 3cb99d8..0f78223 100644 --- a/src/common/cpp_essentials/edge_tracer.rs +++ b/src/common/cpp_essentials/edge_tracer.rs @@ -126,6 +126,13 @@ impl BitMatrixCursor for EdgeTracer<'_> { res } + fn turnedBack(&self) -> Self { + let mut res = self.clone(); + res.d = res.back(); + + res + } + /** * @brief stepToEdge advances cursor to one step behind the next (or n-th) edge. * @param nth number of edges to pass @@ -134,9 +141,9 @@ impl BitMatrixCursor for EdgeTracer<'_> { * @return number of steps taken or 0 if moved outside of range/image */ fn stepToEdge(&mut self, nth: Option, range: Option, backup: Option) -> i32 { - let mut nth = if let Some(nth) = nth { nth } else { 1 }; - let range = if let Some(r) = range { r } else { 0 }; - let backup = if let Some(b) = backup { b } else { false }; + let mut nth = nth.unwrap_or(1); //if let Some(nth) = nth { nth } else { 1 }; + let range = range.unwrap_or(0);//if let Some(r) = range { r } else { 0 }; + let backup = backup.unwrap_or(false);//if let Some(b) = backup { b } else { false }; // TODO: provide an alternative and faster out-of-bounds check than isIn() inside testAt() let mut steps = 0; let mut lv = self.testAt(self.p); @@ -155,6 +162,11 @@ impl BitMatrixCursor for EdgeTracer<'_> { self.p += self.d * steps; steps * i32::from(nth == 0) } + + fn p(&self) -> Point { + self.p + } + } impl<'a> EdgeTracer<'_> { diff --git a/src/common/cpp_essentials/fast_edge_to_edge_counter.rs b/src/common/cpp_essentials/fast_edge_to_edge_counter.rs new file mode 100644 index 0000000..835cb2a --- /dev/null +++ b/src/common/cpp_essentials/fast_edge_to_edge_counter.rs @@ -0,0 +1,40 @@ +use super::BitMatrixCursor; + +pub struct FastEdgeToEdgeCounter { + // const uint8_t* p = nullptr; + // int stride = 0; + // int stepsToBorder = 0; +} + +impl FastEdgeToEdgeCounter +{ + pub fn new(cur: &T) -> Self { + todo!() + // stride = cur.d.y * cur.img->width() + cur.d.x; + // p = cur.img->row(cur.p.y).begin() + cur.p.x; + + // int maxStepsX = cur.d.x ? (cur.d.x > 0 ? cur.img->width() - 1 - cur.p.x : cur.p.x) : INT_MAX; + // int maxStepsY = cur.d.y ? (cur.d.y > 0 ? cur.img->height() - 1 - cur.p.y : cur.p.y) : INT_MAX; + // stepsToBorder = std::min(maxStepsX, maxStepsY); + } + + pub fn stepToNextEdge(&self, range: i32) -> i32 + { + todo!() + // int maxSteps = std::min(stepsToBorder, range); + // int steps = 0; + // do { + // if (++steps > maxSteps) { + // if (maxSteps == stepsToBorder) + // break; + // else + // return 0; + // } + // } while (p[steps * stride] == p[0]); + + // p += steps * stride; + // stepsToBorder -= steps; + + // return steps; + } +} \ No newline at end of file diff --git a/src/common/cpp_essentials/mod.rs b/src/common/cpp_essentials/mod.rs index e306a78..3f040c0 100644 --- a/src/common/cpp_essentials/mod.rs +++ b/src/common/cpp_essentials/mod.rs @@ -8,6 +8,7 @@ pub mod regression_line; pub mod step_result; pub mod util; pub mod value; +pub mod fast_edge_to_edge_counter; pub use bitmatrix_cursor::*; pub use concentric_finder::*; @@ -19,3 +20,4 @@ pub use regression_line::*; pub use step_result::*; pub use util::*; pub use value::*; +pub use fast_edge_to_edge_counter::*; diff --git a/src/common/cpp_essentials/pattern.rs b/src/common/cpp_essentials/pattern.rs index b94ef0f..ff5cf36 100644 --- a/src/common/cpp_essentials/pattern.rs +++ b/src/common/cpp_essentials/pattern.rs @@ -8,17 +8,49 @@ use crate::{common::Result, Exceptions}; pub type PatternType = u16; pub type Pattern = [PatternType; N]; -#[derive(Default)] +#[derive(Default,Debug)] pub struct PatternRow(Vec); // pub struct PatternRow + Into + Copy>(Vec); impl PatternRow { +pub fn new( v: Vec) -> Self { + Self(v) +} + +pub fn len(&self) -> usize { + self.0.len() +} + pub fn into_pattern_view(&self) -> PatternView { PatternView::new(self) } } +impl IntoIterator for PatternRow { + type Item = PatternType; + + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl std::ops::Index for PatternRow { + type Output = PatternType; + + fn index(&self, index: usize) -> &Self::Output { + &self.0[index] + } +} + +impl std::ops::IndexMut for PatternRow { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.0[index] + } +} + impl<'a> Iterator for PatternView<'_> { type Item = PatternType; @@ -33,6 +65,7 @@ impl<'a> Iterator for PatternView<'_> { } } +#[derive(Debug, Clone, Copy)] pub struct PatternView<'a> { data: &'a PatternRow, start: usize, @@ -96,7 +129,7 @@ impl<'a> PatternView<'_> { // index is the number of bars and spaces from the first bar to the current position pub fn index(&self) -> usize { - self.current - self.start - 1 /*return narrow_cast(_data - _base) - 1;*/ + self.current - self.start /*return narrow_cast(_data - _base) - 1;*/ } pub fn pixelsInFront(&self) -> PatternType { self.data @@ -183,7 +216,7 @@ impl<'a> PatternView<'_> { // } pub fn shift(&mut self, n: usize) -> bool { - self.start += n; + self.current += n; !self.data.0.is_empty() && self.start + self.count <= (self.start + self.count) } @@ -234,7 +267,15 @@ impl<'a> std::ops::Index for PatternView<'_> { if index > self.data.0.len() { panic!("array index out of bounds") } - self.data.0.get(self.start + self.current).unwrap() + self.data.0.get(self.start + self.current + index).unwrap() + } +} + +impl<'a> std::ops::Index for PatternView<'_> { + type Output = PatternType; + + fn index(&self, index: i32) -> &Self::Output { + std::ops::Index::::index(self, index as isize) } } @@ -283,7 +324,7 @@ impl std::ops::IndexMut for BarAndSpace // bool isValid() const { return bar != T{} && space != T{}; } // }; -type BarAndSpaceI = BarAndSpace; +type BarAndSpaceI = BarAndSpace; /** * @brief FixedPattern describes a compile-time constant (start/stop) pattern. @@ -293,15 +334,19 @@ type BarAndSpaceI = BarAndSpace; * @param IS_SPARCE whether or not the pattern contains '0's denoting 'wide' bars/spaces */ pub struct FixedPattern { - data: [u16; N], + data: [PatternType; N], } impl FixedPattern { - fn new(data: [u16; N]) -> Self { + pub fn new(data: [PatternType; N]) -> Self { FixedPattern { data } } - fn as_slice(&self) -> &[u16] { + pub fn with_reference(data: &[PatternType; N]) -> Self { + FixedPattern { data: data.clone() } + } + + fn as_slice(&self) -> &[PatternType] { &self.data } @@ -313,7 +358,7 @@ impl FixedPattern std::ops::Index for FixedPattern { - type Output = u16; + type Output = PatternType; fn index(&self, index: usize) -> &Self::Output { &self.data[index] @@ -419,10 +464,6 @@ pub fn FindLeftGuardBy<'a, const LEN: usize, Pred: Fn(&PatternView, Option) window.skipPair(); } - // for (auto end = view.end() - minSize; window.data() < end; window.skipPair()) - // { - - // } Err(Exceptions::ILLEGAL_STATE) } @@ -483,7 +524,7 @@ pub fn NormalizedPattern<'a, const LEN: usize, const SUM: usize>( .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)) }; let mi = mi.ok_or(Exceptions::ILLEGAL_STATE)?; - is[*mi as usize] += err as u16; + is[*mi as usize] += err as PatternType; rs[*mi as usize] -= err as f32; } @@ -496,17 +537,16 @@ enum Color { Black = 1 } -impl> From for Color { +impl> From for Color { fn from(value: T) -> Self { match value.into() { 0 => Color::White, _ => Color::Black, - _=>Color::Black } } } -fn GetPatternRow + Copy + Default + From>(b_row: &[T], p_row: &mut PatternRow) { +fn GetPatternRow + Copy + Default + From>(b_row: &[T], p_row: &mut PatternRow) { p_row.0.clear(); if Color::from(p_row.0.first().copied().unwrap_or_default()) == Color::Black { @@ -542,20 +582,22 @@ fn GetPatternRow + Copy + Default + From>(b_row: &[T], p_row: &m #[cfg(test)] mod tests { - use super::{GetPatternRow, PatternRow}; + use crate::common::cpp_essentials::PatternType; + + use super::{GetPatternRow, PatternRow, PatternView}; const N: usize = 33; #[test] fn all_white() { for s in 1..=N { // for (int s = 1; s <= N; ++s) { - let t_in = vec![0_u16; s]; + let t_in:Vec = vec![0; s]; // std::vector in(s, 0); let mut pr = PatternRow::default(); GetPatternRow(&t_in, &mut pr); assert_eq!(pr.0.len(), 1); - assert_eq!(pr.0[0], s as u16); + assert_eq!(pr.0[0], s as PatternType); } } @@ -563,13 +605,13 @@ mod tests { fn all_black() { for s in 1..=N { // for (int s = 1; s <= N; ++s) { - let t_in: Vec = vec![0xff; s]; + let t_in: Vec = vec![0xff; s]; let mut pr = PatternRow::default(); GetPatternRow(&t_in, &mut pr); assert_eq!(pr.0.len(), 3); assert_eq!(pr.0[0], 0); - assert_eq!(pr.0[1], s as u16); + assert_eq!(pr.0[1], s as PatternType); assert_eq!(pr.0[2], 0); } } @@ -578,7 +620,7 @@ mod tests { fn black_white() { for s in 1..=N { // for (int s = 1; s <= N; ++s) { - let mut t_in = vec![0_u16; N]; + let mut t_in : Vec = vec![0; N]; t_in[..s].copy_from_slice(&vec![1; s]); // std::fill_n(in.data(), s, 0xff); let mut pr = PatternRow::default(); @@ -586,8 +628,8 @@ mod tests { assert_eq!(pr.0.len(), 3); assert_eq!(pr.0[0], 0); - assert_eq!(pr.0[1], s as u16); - assert_eq!(pr.0[2], (N - s) as u16); + assert_eq!(pr.0[1], s as PatternType); + assert_eq!(pr.0[2], (N - s) as PatternType); } } @@ -595,15 +637,36 @@ mod tests { fn white_black() { for s in 0..N { // for (int s = 0; s < N; ++s) { - let mut t_in: Vec = vec![0xff; N]; + let mut t_in: Vec = vec![0xff; N]; t_in[..s].copy_from_slice(&vec![0; s]); let mut pr = PatternRow::default(); GetPatternRow(&t_in, &mut pr); assert_eq!(pr.0.len(), 3); - assert_eq!(pr.0[0], s as u16); - assert_eq!(pr.0[1], (N - s) as u16); + assert_eq!(pr.0[0], s as PatternType); + assert_eq!(pr.0[1], (N - s) as PatternType); assert_eq!(pr.0[2], 0); } } + + #[test] + fn basic_pattern_view() { + let mut p_row = PatternRow::default(); + GetPatternRow(&vec![0_u16,1,0,1,0,0,1,1,1,0,0,1,1,1,1,1,1,0,0,0,0,1], &mut p_row); + + let mut pv = PatternView::new(&p_row); + + assert_eq!(pv.data().0,p_row.0); + + assert_eq!(pv[0], 1_u16); + assert_eq!(pv[1], 1_u16); + assert_eq!(pv[4], 2_u16); + assert_eq!(pv[7], 6_u16); + + assert_eq!(pv.index(), 0); + assert!(pv.shift(1)); + assert_eq!(pv.index(), 1); + assert!(pv.skipPair()); + assert_eq!(pv.index(),3); + } } diff --git a/src/common/cpp_essentials/regression_line.rs b/src/common/cpp_essentials/regression_line.rs index a9ccd90..a859480 100644 --- a/src/common/cpp_essentials/regression_line.rs +++ b/src/common/cpp_essentials/regression_line.rs @@ -1,5 +1,5 @@ use crate::common::Result; -use crate::Point; +use crate::{Point, point}; pub trait RegressionLine { // points: Vec, @@ -11,8 +11,18 @@ pub trait RegressionLine { // PointF _directionInward; // PointF::value_t a = NAN, b = NAN, c = NAN; - // fn intersect(&self, l1: &T, l2: &T2) - // -> Point; + fn intersect( l1: &T, l2: &T2) + -> Option{ + if !(l1.isValid() && l2.isValid()) { + return None; + } + + let d = l1.a() * l2.b() - l1.b() * l2.a(); + let x = (l1.c() * l2.b() - l1.b() * l2.c()) / d; + let y = (l1.a() * l2.c() - l1.c() * l2.a()) / d; + + Some(point(x, y)) + } // fn evaluate_begin_end(&self, begin: Point, end: Point) -> bool;// { // { @@ -131,4 +141,7 @@ pub trait RegressionLine { // // due to aliasing we get bad extrapolations if the line is short and too close to vertical/horizontal // return steps > 2 || len > 50; // } + fn a(&self) -> f32; + fn b(&self) -> f32; + fn c(&self) -> f32; } diff --git a/src/common/quad.rs b/src/common/quad.rs index 688a1dd..3d33ad2 100644 --- a/src/common/quad.rs +++ b/src/common/quad.rs @@ -208,6 +208,28 @@ impl Quadrilateral { !(x || y) } + + pub fn blend(a: &Quadrilateral, b:&Quadrilateral) -> Self { + let c = a[0]; + let dist2First = | a, b| { Point::distance(a, c) < Point::distance(b, c) }; + // rotate points such that the the two topLeft points are closest to each other + let min_element = b.0.iter().copied().min_by(|a,b| { + match dist2First(*a,*b) { + true => std::cmp::Ordering::Less, + false => std::cmp::Ordering::Greater, + } + }).unwrap_or_default(); + let offset = b.0.iter().position(|v| *v == min_element).unwrap_or_default(); + // let offset = std::min_element(b.begin(), b.end(), dist2First) - b.begin(); + + let mut res= Quadrilateral::default(); + for i in 0..4 { + // for (int i = 0; i < 4; ++i){ + res[i] = (a[i] + b[(i + offset) % 4]) / 2.0; + } + + res + } } impl Default for Quadrilateral { @@ -215,3 +237,17 @@ impl Default for Quadrilateral { Self([Point { x: 0.0, y: 0.0 }; 4]) } } + +impl std::ops::Index for Quadrilateral { + type Output = Point; + + fn index(&self, index: usize) -> &Self::Output { + &self.0[index] + } +} + +impl std::ops::IndexMut for Quadrilateral { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.0[index] + } +} \ No newline at end of file diff --git a/src/rxing_result_point.rs b/src/rxing_result_point.rs index 78695c7..da5e401 100644 --- a/src/rxing_result_point.rs +++ b/src/rxing_result_point.rs @@ -117,6 +117,22 @@ impl std::ops::Add for Point { } } +impl std::ops::Add for Point { + type Output = Self; + + fn add(self, rhs: f32) -> Self::Output { + Self::new(self.x + rhs, self.y + rhs) + } +} + +impl std::ops::Add for f32 { + type Output = Point; + + fn add(self, rhs: Point) -> Self::Output { + Point::new(rhs.x + self, rhs.y + self) + } +} + impl std::ops::Mul for Point { type Output = Self; @@ -141,6 +157,14 @@ impl std::ops::Mul for Point { } } +impl std::ops::Mul for Point { + type Output = Self; + + fn mul(self, rhs: u32) -> Self::Output { + Self::new(self.x * rhs as f32, self.y * rhs as f32) + } +} + impl std::ops::Mul for i32 { type Output = Point; @@ -165,6 +189,14 @@ impl std::ops::Div for Point { } } +impl std::ops::Mul for u32 { + type Output = Point; + + fn mul(self, rhs: Point) -> Self::Output { + Self::Output::new(rhs.x * self as f32, rhs.y * self as f32) + } +} + impl Point { pub fn dot(self, p: Self) -> f32 { self.x * p.x + self.y * p.y