예제 #1
0
        public void BoxReflectInPlaneTest()
        {
            Box3d          b = new Box3d();
            List <Point3d> original_points = b.ListOfPoints;

            Plane3d        s                = new Plane3d(new Point3d(-4.1, 7.876, -8), new Vector3d(1.25, -8, -22));
            Box3d          reflected_box    = b.ReflectIn(s);
            List <Point3d> reflected_points = reflected_box.ListOfPoints;

            foreach (Point3d op in original_points)
            {
                Point3d reflected_p = op.ReflectIn(s);
                foreach (Point3d rp in reflected_points)
                {
                    if (reflected_p == rp)
                    {
                        reflected_points.Remove(rp);
                        break;
                    }
                }
            }

            Assert.IsTrue(reflected_points.Count == 0);
        }
예제 #2
0
        /// <summary>
        /// Transforms points from plane space to world space.
        /// </summary>
        public static V3d[] Unproject(this Plane3d plane, V2d[] points)
        {
            var local2global = plane.GetPlaneToWorld();

            return(points.Map(p => local2global.TransformPos(p.XYO)));
        }
예제 #3
0
 /// <summary>
 /// Performs RANSAC consensus plane fitting.
 /// Returns a least-squares fitted plane to the inliers.
 /// </summary>
 /// <param name="points">Point Array - here the most dominant plane will be fitted</param>
 /// <param name="iterations">Number of RANSAC iterations: depends on the expected ratio between inliers and outliers. Independent of point array's length.</param>
 /// <param name="min_consensus">Minimum number of inlier points to be in the plane</param>
 /// <param name="epsilon">The +-distance of the points to the plane to be considered (absolute value)</param>
 /// <param name="plane">returns the fitted plane</param>
 /// <param name="inliers"> Returns the indices of inlier-points </param>
 /// <returns>returns true if a valid plane was found</returns>
 public static bool FitPlane3dRansac(this V3d[] points, int iterations, int min_consensus, double epsilon,
                                     out Plane3d plane, out int[] inliers)
 {
     plane = FitPlane3dRansac(points, iterations, min_consensus, epsilon, out inliers);
     return(!plane.IsInvalid);
 }
예제 #4
0
 /// <summary>
 /// Least squares plane fitting to a set of points.
 /// </summary>
 public static bool FitPlane3dLeastMedianOfSquares(this V3d[] points, out Plane3d plane)
 {
     plane = FitPlane3dLeastSquares(points);
     return(!plane.IsInvalid);
 }
예제 #5
0
        /// <summary>
        /// Perform one step of the weighted least squares plane fitting
        /// </summary>
        /// <param name="points">Data points</param>
        /// <param name="weights">Corresponding point weights</param>
        /// <returns></returns>
        private static Plane3d PerformOneStep(V3d[] points, double[] weights)
        {
            if (points == null)
            {
                throw new ArgumentNullException(nameof(points));
            }
            if (weights == null)
            {
                throw new ArgumentNullException(nameof(weights));
            }
            if (points.Length != weights.Length)
            {
                throw new InvalidOperationException();
            }

            if (points.Length < 3)
            {
                return(Plane3d.Invalid);
            }

            if (points.Length == 3)
            {
                var t0 = points[1] - points[0];
                var t1 = points[2] - points[0];
                var n  = t0.Cross(t1).Normalized;
                return(new Plane3d(n, points[0]));
            }

            V3d c = V3d.Zero;

            for (var i = 0; i < points.Length; ++i)
            {
                c += weights[i] * points[i];
            }
            c /= weights.Sum();


            var data = new double[points.Length * 3];

            for (int i = 0; i < points.Length; ++i)
            {
                data[3 * i + 0] = Fun.Sqrt(weights[i]) * (points[i].X - c.X);
                data[3 * i + 1] = Fun.Sqrt(weights[i]) * (points[i].Y - c.Y);
                data[3 * i + 2] = Fun.Sqrt(weights[i]) * (points[i].Z - c.Z);
            }

            var A = new Matrix <double>(data, new V2i(3, points.Length));

            A.ComputeSingularValues(out Matrix <double> UT, out Matrix <double> VT, out Matrix <double> S, true);
            var normal = new V3d(VT.SubXVectorWindow(2).CopyWindow().Data);

            var plane = new Plane3d(normal.Normalized, c);

            //-- set the normal to be on the same side as the origin
            if (plane.Sign(V3d.OOO) < 0)
            {
                plane.Reverse();
            }

            return(plane);
        }
예제 #6
0
 /// <summary>
 /// Perform weighted least squares plane fitting
 /// </summary>
 /// <param name="points">Data points</param>
 /// <param name="maxIter">Maximum number of iterations</param>
 /// <param name="thres">The algorithm terminates if the change in mean squared error
 /// is below the given threshold</param>
 /// <param name="mse">Mean squared error</param>
 /// <param name="plane">The found plane</param>
 /// <returns>True if a valid plane was found</returns>
 public static bool FitPlane3dWeightedLeastSquares(this V3d[] points, int maxIter, double thres, out double mse, out Plane3d plane)
 {
     plane = FitPlane3dWeightedLeastSquares(points, maxIter, thres, out mse);
     return(!plane.IsInvalid);
 }
예제 #7
0
 public static FloatL PlaneEquation(Vector3L point, Plane3d plane)
 {
     return(Vector3L.Dot(point, plane.m_planeNormal) - plane.GetDistanceFromOrigin());
 }
예제 #8
0
        /// <summary>
        /// Clips the mesh with the given plane.
        /// The part on the positive hals will remain.
        /// Will return null when everything is clipped.
        /// Cap-faces will be built on everything that is cut open by the plane (non-convex cap faces are not handled properly).
        /// Only works for meshes without Face-/FaceVertexAttributes -> attributes will be invalid for generated faces.
        /// </summary>
        public PolyMesh ClipByPlane(Plane3d plane, double epsilon = 1e-7)
        {
            var clippedMesh = SplitOnPlane(plane, epsilon).Item2;

            // in case everything is clipped away
            if (clippedMesh == null)
            {
                return(null);
            }

            // 1. go trough all edges
            // 2. if edge on plane -> test if there is an open face along the plane (border edges)

            var vertexOnPlane = clippedMesh.PositionArray.Map(clippedMesh.VertexCount, p => plane.Height(p).Abs() <= epsilon);

            clippedMesh.BuildTopology();

            // use indices so an edge can be removed no matter what "ref"
            var edges = new IntSet(clippedMesh.EdgeCount);

            foreach (var e in clippedMesh.Edges)
            {
                if (e.IsValid)
                {
                    edges.Add(e.Index);
                }
            }

            var capFaceEdges   = new List <PolyMesh.Edge>();
            var capFaceEdgeSet = new IntSet();

            while (edges.Count > 0)
            {
                var e = clippedMesh.GetEdge(edges.First());
                edges.Remove(e.Index);

                if (e.IsAnyBorder &&
                    vertexOnPlane[e.FromVertexIndex] &&
                    vertexOnPlane[e.ToVertexIndex])
                {
                    // try to find other edges along the plane
                    // keep set of all edges so in case there the loop runs into a degenerated case an exit is possible (will generate degenerated face)
                    capFaceEdges.Clear();
                    capFaceEdgeSet.Clear();

                    var currEdge = e;
                    do
                    {
                        if (currEdge.IsValid)
                        {
                            capFaceEdges.Add(currEdge);
                            capFaceEdgeSet.Add(currEdge.Index);
                        }

                        // find next edge at start-vertex along plane
                        // the new cap face should have winding order start<-to becaues it is on the opposite of the current face-edge
                        currEdge = currEdge.FromVertex.Edges
                                   .Select(x => x.FromVertexIndex == currEdge.FromVertexIndex ? x.Opposite : x)
                                   .FirstOrDefault(x => x.IsAnyBorder && x.Index != currEdge.Index && vertexOnPlane[x.FromVertexIndex], PolyMesh.Edge.Invalid);
                    } while (currEdge.IsValid &&
                             currEdge.Index != e.Index &&
                             !capFaceEdgeSet.Contains(currEdge.Index));

                    if (capFaceEdges.Count > 2)
                    {
                        // add cap-face
                        foreach (var fe in capFaceEdges.Skip(1))
                        {
                            edges.Remove(fe.Index);
                        }

                        clippedMesh.AddFace(capFaceEdges.Select(fe => fe.ToVertexIndex).ToArray());
                    }
                }
            }

            // clear topology (is invalid if face has been added)
            clippedMesh.ClearTopology();

            return(clippedMesh);
        }
예제 #9
0
 public void CodePlane3d(ref Plane3d v)
 {
     throw new NotImplementedException();
 }
예제 #10
0
 public void CodePlane3d(ref Plane3d v)
 {
     AddValue(v.ToString());
 }
예제 #11
0
 /// <summary>
 /// Returns true if this node is fully inside the negative halfspace defined by given plane.
 /// </summary>
 public static bool InsideNegativeHalfSpace(this PointSetNode self, Plane3d plane)
 {
     self.BoundingBox.GetMinMaxInDirection(-plane.Normal, out V3d min, out V3d max);
     return(plane.Height(min) < 0);
 }
예제 #12
0
 /// <summary>
 /// Returns true if this node intersects the negative halfspace defined by given plane.
 /// </summary>
 public static bool IntersectsNegativeHalfSpace(this PointSetNode self, Plane3d plane)
 => self.Corners.Any(p => plane.Height(p) < 0);
예제 #13
0
 /// <summary>
 /// Returns true if this node intersects the space within a given distance to a plane.
 /// </summary>
 public static bool Intersects(this PointSetNode self, Plane3d plane, double distance)
 => self.BoundingBox.Intersects(plane, distance);
예제 #14
0
 public static Segment3d Unproject(this Plane3d _plane, Segment2d _seg)
 {
     return(new Segment3d(_plane.Evaluate(_seg.P0), _plane.Evaluate(_seg.P1)));
 }
예제 #15
0
 /// <summary>Set the <see cref="Position"/> and <see cref="Direction"/> parameters using the <see cref="Plane3d"/>.</summary>
 /// <param name="plane"></param>
 public void Setup(Plane3d plane)
 {
     Direction = plane.Normal; Position = plane.Distance * direction;
 }
예제 #16
0
        /// <summary>
        /// Splits the mesh on the specified plane in a pair containing the negative
        /// (element 0) and the positive side (element 1) of the plane.
        /// Note that the normal vector of the plane need not be normalized.
        /// </summary>
        public (PolyMesh, PolyMesh) SplitOnPlane(
            Plane3d plane, double epsilon, SplitterOptions options)
        {
            var heightArray = m_positionArray.Map(
                m_vertexCount, p => plane.Height(p));
            var splitter = new PolygonSplitter(
                m_firstIndexArray, m_faceCount,
                m_vertexIndexArray, m_faceVertexCountRange.Max,
                heightArray, epsilon, options);

            var result = (default(PolyMesh), default(PolyMesh));

            for (int side = 0; side < 2; side++)
            {
                var fia = splitter.FirstIndexArray(side);
                if (fia != null)
                {
                    if (splitter.IsEqualToInput(side))
                    {
                        //result[side] = this;
                        switch (side)
                        {
                        case 0:
                            result = (this, result.Item2);
                            break;

                        case 1:
                            result = (result.Item1, this);
                            break;

                        default:
                            throw new IndexOutOfRangeException();
                        }
                    }
                    else
                    {
                        var pm = new PolyMesh()
                        {
                            FirstIndexArray    = fia,
                            VertexIndexArray   = splitter.VertexIndexArray(side),
                            InstanceAttributes = InstanceAttributes,
                            FaceAttributes     = FaceIAttributes.Select(
                                a => splitter.FaceAttribute(side, a)).ToSymbolDict(),
                            VertexAttributes = VertexIAttributes.Select(
                                a => splitter.VertexAttribute(side, a)).ToSymbolDict(),
                            FaceVertexAttributes = FaceVertexIAttributes.Select(
                                a => splitter.FaceVertexAttribute(side, a)).ToSymbolDict(),
                        };

                        //result[side] = pm;
                        switch (side)
                        {
                        case 0:
                            result = (pm, result.Item2);
                            break;

                        case 1:
                            result = (result.Item1, pm);
                            break;

                        default:
                            throw new IndexOutOfRangeException();
                        }
                    }
                }
            }

            return(result);
        }
예제 #17
0
 /// <summary>
 /// Splits the mesh on the specified plane in a pair containing the negative
 /// (element 0) and the positive side (element 1) of the plane.
 /// Note that the normal vector of the plane need not be normalized.
 /// </summary>
 public (PolyMesh, PolyMesh) SplitOnPlane(
     Plane3d plane, double epsilon)
 {
     return(SplitOnPlane(plane, epsilon, SplitterOptions.NegativeAndPositive));
 }
예제 #18
0
        bool IntersectsSegment(ref Plane3d plane, ref Triangle3d triangle, Vector3D end0, Vector3D end1)
        {
            // Compute the 2D representations of the triangle vertices and the
            // segment endpoints relative to the plane of the triangle.  Then
            // compute the intersection in the 2D space.

            // Project the triangle and segment onto the coordinate plane most
            // aligned with the plane normal.
            int    maxNormal = 0;
            double fmax      = Math.Abs(plane.Normal.x);
            double absMax    = Math.Abs(plane.Normal.y);

            if (absMax > fmax)
            {
                maxNormal = 1;
                fmax      = absMax;
            }
            absMax = Math.Abs(plane.Normal.z);
            if (absMax > fmax)
            {
                maxNormal = 2;
            }

            Triangle2d projTri = new Triangle2d();
            Vector2D   projEnd0 = Vector2D.Zero, projEnd1 = Vector2D.Zero;
            int        i;

            if (maxNormal == 0)
            {
                // Project onto yz-plane.
                for (i = 0; i < 3; ++i)
                {
                    projTri[i] = triangle[i].yz;
                    projEnd0.x = end0.y;
                    projEnd0.y = end0.z;
                    projEnd1.x = end1.y;
                    projEnd1.y = end1.z;
                }
            }
            else if (maxNormal == 1)
            {
                // Project onto xz-plane.
                for (i = 0; i < 3; ++i)
                {
                    projTri[i] = triangle[i].xz;
                    projEnd0.x = end0.x;
                    projEnd0.y = end0.z;
                    projEnd1.x = end1.x;
                    projEnd1.y = end1.z;
                }
            }
            else
            {
                // Project onto xy-plane.
                for (i = 0; i < 3; ++i)
                {
                    projTri[i] = triangle[i].xy;
                    projEnd0.x = end0.x;
                    projEnd0.y = end0.y;
                    projEnd1.x = end1.x;
                    projEnd1.y = end1.y;
                }
            }

            Segment2d             projSeg = new Segment2d(projEnd0, projEnd1);
            IntrSegment2Triangle2 calc    = new IntrSegment2Triangle2(projSeg, projTri);

            if (!calc.Find())
            {
                return(false);
            }

            Vector2dTuple2 intr = new Vector2dTuple2();

            if (calc.Type == IntersectionType.Segment)
            {
                Result   = IntersectionResult.Intersects;
                Type     = IntersectionType.Segment;
                Quantity = 2;
                intr.V0  = calc.Point0;
                intr.V1  = calc.Point1;
            }
            else
            {
                Debug.Assert(calc.Type == IntersectionType.Point);
                //"Intersection must be a point\n";
                Result   = IntersectionResult.Intersects;
                Type     = IntersectionType.Point;
                Quantity = 1;
                intr.V0  = calc.Point0;
            }

            // Unproject the segment of intersection.
            if (maxNormal == 0)
            {
                double invNX = ((double)1) / plane.Normal.x;
                for (i = 0; i < Quantity; ++i)
                {
                    double y = intr[i].x;
                    double z = intr[i].y;
                    double x = invNX * (plane.Constant - plane.Normal.y * y - plane.Normal.z * z);
                    Points[i] = new Vector3D(x, y, z);
                }
            }
            else if (maxNormal == 1)
            {
                double invNY = ((double)1) / plane.Normal.y;
                for (i = 0; i < Quantity; ++i)
                {
                    double x = intr[i].x;
                    double z = intr[i].y;
                    double y = invNY * (plane.Constant - plane.Normal.x * x - plane.Normal.z * z);
                    Points[i] = new Vector3D(x, y, z);
                }
            }
            else
            {
                double invNZ = ((double)1) / plane.Normal.z;
                for (i = 0; i < Quantity; ++i)
                {
                    double x = intr[i].x;
                    double y = intr[i].y;
                    double z = invNZ * (plane.Constant - plane.Normal.x * x - plane.Normal.y * y);
                    Points[i] = new Vector3D(x, y, z);
                }
            }

            return(true);
        }
예제 #19
0
 /// <summary>
 /// Splits the supplied PolyMeshes on the specified plane and
 /// returns the desired results (specified by the options parameter)
 /// in a pair containing lists of PolyMeshes for the negative
 /// (element 0) and the positive side (element 1) of the plane.
 /// Note that the normal vector of the plane need not be normalized.
 /// </summary>
 public static (List <PolyMesh>, List <PolyMesh>) SplitOnPlane(
     this IEnumerable <PolyMesh> polyMeshes,
     Plane3d plane, double epsilon, SplitterOptions options)
 {
     return(polyMeshes.SplitOnPlane(plane, epsilon, options, s_polyMeshSplitOnPlane));
 }
예제 #20
0
        bool GetCoplanarIntersection(ref Plane3d plane, ref Triangle3d tri0, ref Triangle3d tri1)
        {
            // Project triangles onto coordinate plane most aligned with plane
            // normal.
            int    maxNormal = 0;
            double fmax      = Math.Abs(plane.Normal.x);
            double absMax    = Math.Abs(plane.Normal.y);

            if (absMax > fmax)
            {
                maxNormal = 1;
                fmax      = absMax;
            }
            absMax = Math.Abs(plane.Normal.z);
            if (absMax > fmax)
            {
                maxNormal = 2;
            }

            Triangle2d projTri0 = new Triangle2d(), projTri1 = new Triangle2d();
            int        i;

            if (maxNormal == 0)
            {
                // Project onto yz-plane.
                for (i = 0; i < 3; ++i)
                {
                    projTri0[i] = tri0[i].yz;
                    projTri1[i] = tri1[i].yz;
                }
            }
            else if (maxNormal == 1)
            {
                // Project onto xz-plane.
                for (i = 0; i < 3; ++i)
                {
                    projTri0[i] = tri0[i].xz;
                    projTri1[i] = tri1[i].xz;
                }
            }
            else
            {
                // Project onto xy-plane.
                for (i = 0; i < 3; ++i)
                {
                    projTri0[i] = tri0[i].xy;
                    projTri1[i] = tri1[i].xy;
                }
            }

            // 2D triangle intersection routines require counterclockwise ordering.
            Vector2D save;
            Vector2D edge0 = projTri0[1] - projTri0[0];
            Vector2D edge1 = projTri0[2] - projTri0[0];

            if (edge0.DotPerp(edge1) < (double)0)
            {
                // Triangle is clockwise, reorder it.
                save        = projTri0[1];
                projTri0[1] = projTri0[2];
                projTri0[2] = save;
            }

            edge0 = projTri1[1] - projTri1[0];
            edge1 = projTri1[2] - projTri1[0];
            if (edge0.DotPerp(edge1) < (double)0)
            {
                // Triangle is clockwise, reorder it.
                save        = projTri1[1];
                projTri1[1] = projTri1[2];
                projTri1[2] = save;
            }

            IntrTriangle2Triangle2 intr = new IntrTriangle2Triangle2(projTri0, projTri1);

            if (!intr.Find())
            {
                return(false);
            }

            PolygonPoints = new Vector3D[intr.Quantity];

            // Map 2D intersections back to the 3D triangle space.
            Quantity = intr.Quantity;
            if (maxNormal == 0)
            {
                double invNX = ((double)1) / plane.Normal.x;
                for (i = 0; i < Quantity; i++)
                {
                    double y = intr.Points[i].x;
                    double z = intr.Points[i].y;
                    double x = invNX * (plane.Constant - plane.Normal.y * y - plane.Normal.z * z);
                    PolygonPoints[i] = new Vector3D(x, y, z);
                }
            }
            else if (maxNormal == 1)
            {
                double invNY = ((double)1) / plane.Normal.y;
                for (i = 0; i < Quantity; i++)
                {
                    double x = intr.Points[i].x;
                    double z = intr.Points[i].y;
                    double y = invNY * (plane.Constant - plane.Normal.x * x - plane.Normal.z * z);
                    PolygonPoints[i] = new Vector3D(x, y, z);
                }
            }
            else
            {
                double invNZ = ((double)1) / plane.Normal.z;
                for (i = 0; i < Quantity; i++)
                {
                    double x = intr.Points[i].x;
                    double y = intr.Points[i].y;
                    double z = invNZ * (plane.Constant - plane.Normal.x * x - plane.Normal.y * y);
                    PolygonPoints[i] = new Vector3D(x, y, z);
                }
            }

            Result = IntersectionResult.Intersects;
            Type   = IntersectionType.Polygon;
            return(true);
        }
예제 #21
0
 public void CodePlane3d(ref Plane3d v)
 {
     CodeV3d(ref v.Normal); CodeDouble(ref v.Distance);
 }
예제 #22
0
        public bool Find()
        {
            if (Result != IntersectionResult.NotComputed)
            {
                return(Result != IntersectionResult.NoIntersection);
            }


            // in this code the results get initialized in subroutines, so we
            // set the defautl value here...
            Result = IntersectionResult.NoIntersection;


            int i, iM, iP;

            // Get the plane of triangle0.
            Plane3d plane0 = new Plane3d(triangle0.V0, triangle0.V1, triangle0.V2);

            // Compute the signed distances of triangle1 vertices to plane0.  Use
            // an epsilon-thick plane test.
            int      pos1, neg1, zero1;
            Index3i  sign1;
            Vector3D dist1;

            TrianglePlaneRelations(ref triangle1, ref plane0, out dist1, out sign1, out pos1, out neg1, out zero1);

            if (pos1 == 3 || neg1 == 3)
            {
                // Triangle1 is fully on one side of plane0.
                return(false);
            }

            if (zero1 == 3)
            {
                // Triangle1 is contained by plane0.
                if (ReportCoplanarIntersection)
                {
                    return(GetCoplanarIntersection(ref plane0, ref triangle0, ref triangle1));
                }
                return(false);
            }

            // Check for grazing contact between triangle1 and plane0.
            if (pos1 == 0 || neg1 == 0)
            {
                if (zero1 == 2)
                {
                    // An edge of triangle1 is in plane0.
                    for (i = 0; i < 3; ++i)
                    {
                        if (sign1[i] != 0)
                        {
                            iM = (i + 2) % 3;
                            iP = (i + 1) % 3;
                            return(IntersectsSegment(ref plane0, ref triangle0, triangle1[iM], triangle1[iP]));
                        }
                    }
                }
                else    // zero1 == 1
                // A vertex of triangle1 is in plane0.
                {
                    for (i = 0; i < 3; ++i)
                    {
                        if (sign1[i] == 0)
                        {
                            return(ContainsPoint(ref triangle0, ref plane0, triangle1[i]));
                        }
                    }
                }
            }

            // At this point, triangle1 tranversely intersects plane 0.  Compute the
            // line segment of intersection.  Then test for intersection between this
            // segment and triangle 0.
            double   t;
            Vector3D intr0, intr1;

            if (zero1 == 0)
            {
                int iSign = (pos1 == 1 ? +1 : -1);
                for (i = 0; i < 3; ++i)
                {
                    if (sign1[i] == iSign)
                    {
                        iM    = (i + 2) % 3;
                        iP    = (i + 1) % 3;
                        t     = dist1[i] / (dist1[i] - dist1[iM]);
                        intr0 = triangle1[i] + t * (triangle1[iM] - triangle1[i]);
                        t     = dist1[i] / (dist1[i] - dist1[iP]);
                        intr1 = triangle1[i] + t * (triangle1[iP] - triangle1[i]);
                        return(IntersectsSegment(ref plane0, ref triangle0, intr0, intr1));
                    }
                }
            }

            // zero1 == 1
            for (i = 0; i < 3; ++i)
            {
                if (sign1[i] == 0)
                {
                    iM    = (i + 2) % 3;
                    iP    = (i + 1) % 3;
                    t     = dist1[iM] / (dist1[iM] - dist1[iP]);
                    intr0 = triangle1[iM] + t * (triangle1[iP] - triangle1[iM]);
                    return(IntersectsSegment(ref plane0, ref triangle0, triangle1[i], intr0));
                }
            }

            // should never get here...
            Debug.Assert(false);
            return(false);
        }
예제 #23
0
 /// <summary>
 /// Perform weighted least squares plane fitting
 /// </summary>
 /// <param name="points">Data points</param>
 /// <param name="weights">Point weights</param>
 /// <param name="plane">Found plane (out)</param>
 /// <returns>True if a valid plane was found</returns>
 public static bool FitPlane3dWeightedLeastSquares(this V3d[] points, double[] weights, out Plane3d plane)
 {
     plane = FitPlane3dWeightedLeastSquares(points, weights);
     return(!plane.IsInvalid);
 }
예제 #24
0
    public static Plane3d FromTriangle(Triangle3d t)
    {
        Plane3d result = new Plane3d(Vector3L.Cross(t.m_point1 - t.m_point0, t.m_point2 - t.m_point0), t.m_point0);

        return(result);
    }
예제 #25
0
        /// <summary>
        /// Perform least median of squares plane fitting.
        /// </summary>
        /// <param name="points">Data points</param>
        /// <returns>The found plane</returns>
        public static Plane3d FitPlane3dLeastMedianOfSquares(this V3d[] points)
        {
            if (points.Length < 3)
            {
                return(Plane3d.Invalid);
            }

            if (points.Length == 3)
            {
                return(new Plane3d(points.First(), points.ElementAt(1), points.Last()));
            }

            var plane = new Plane3d();

            var rnd = new Random();
            var relativeOutlierRatio = 0.4;
            var probabilityOfSuccess = 0.999;
            var numOfIterations      = (int)(Fun.Log(1.0 - probabilityOfSuccess) / Fun.Log(1.0 - (1.0 - relativeOutlierRatio).Pow(3.0)));

            var leastMedianOfSquaredResiduals = double.PositiveInfinity;

            for (int i = 0; i < numOfIterations; i++)
            {
                int  p0Index, p1Index, p2Index;
                bool randomPointsSelected;
                do
                {
                    randomPointsSelected = false;
                    p0Index = rnd.Next(points.Length);
                    p1Index = rnd.Next(points.Length);
                    p2Index = rnd.Next(points.Length);

                    if (p0Index != p1Index && p0Index != p2Index && p1Index != p2Index)
                    {
                        randomPointsSelected = true;
                    }
                } while (!randomPointsSelected);

                var p0 = points[p0Index];
                var p1 = points[p1Index];
                var p2 = points[p2Index];

                // check if points are degenerated -> same position or all along a line -> continue then
                var candidatePlane = new Plane3d(p0, p1, p2);
                if (candidatePlane.Normal.LengthSquared.IsTiny())
                {
                    continue;
                }

                var medianOfSquaredResiduals = points
                                               .Where((p, k) => k != p0Index && k != p1Index && k != p2Index)
                                               .Select(p => candidatePlane.Height(p).Square())
                                               .Median();

                if (medianOfSquaredResiduals < leastMedianOfSquaredResiduals)
                {
                    leastMedianOfSquaredResiduals = medianOfSquaredResiduals;
                    plane = candidatePlane;
                }
            }

            return(plane);
        }
예제 #26
0
        public Rect RectHandle(Transform owner, Rect rect, Color lineColor, Color dotColor)
        {
            Rect resRect = rect;

            Vector2[] cornes =
            {
                new Vector3(rect.x, rect.y),      //corneUpLeft
                new Vector3(rect.xMax, rect.y),   // corneUpRight
                new Vector3(rect.x, rect.yMax),   //corneDownLeft
                new Vector3(rect.xMax, rect.yMax) // corneDownRight
            };
            Vector2[] cornesS = new Vector2[4];
            for (int i = 0; i < 4; i++)
            {
                cornesS[i] = HandleUtility.WorldToGUIPoint(owner.TransformPoint(cornes[i]));
            }
            if (Event.current.type == EventType.MouseDown)
            {
                float   minDis      = float.MaxValue;
                Vector2 mPos        = Event.current.mousePosition;
                int     closestCorn = -1;
                for (int i = 0; i < cornesS.Length; i++)
                {
                    float d = Vector2.Distance(cornesS[i], mPos);
                    if (d < minDis)
                    {
                        minDis      = d;
                        closestCorn = i;
                    }
                }
                if (minDis < 10)
                {
                    selCorner = closestCorn;
                }
                else
                {
                    selCorner = -1;
                }
            }
            if (Event.current.type == EventType.MouseUp)
            {
                selCorner = -1;
            }
            if (Event.current.type == EventType.MouseDrag && selCorner != -1)
            {
                Plane3d plnr = new Plane3d(owner.transform.position, owner.transform.rotation);
                Vector3 wp   = plnr.rayCast(HandleUtility.GUIPointToWorldRay(Event.current.mousePosition));
                Vector2 lm   = owner.InverseTransformPoint(wp);
                if (selCorner == 0)
                {
                    resRect.xMin = lm.x;
                    resRect.yMin = lm.y;
                }
                if (selCorner == 1)
                {
                    resRect.xMax = lm.x;
                    resRect.yMin = lm.y;
                }
                if (selCorner == 2)
                {
                    resRect.xMin = lm.x;
                    resRect.yMax = lm.y;
                }
                if (selCorner == 3)
                {
                    resRect.xMax = lm.x;
                    resRect.yMax = lm.y;
                }
            }
            Vector3[] polyRect =
            {
                owner.TransformPoint(resRect.x,    resRect.y,    0),
                owner.TransformPoint(resRect.xMax, resRect.y,    0),
                owner.TransformPoint(resRect.xMax, resRect.yMax, 0),
                owner.TransformPoint(resRect.x,    resRect.yMax, 0),
                owner.TransformPoint(resRect.x,    resRect.y, 0)
            };
            Handles.color = lineColor;
            Handles.DrawPolyLine(polyRect);
            Handles.color = dotColor;
            foreach (var item in polyRect)
            {
                Handles.DotCap(0, item, owner.rotation, HandleUtility.GetHandleSize(item) * 0.05f);
            }
            return(resRect);
        }
예제 #27
0
 /// <summary>
 /// Performs RANSAC consensus plane fitting.
 /// Returns a least-squares fitted plane to the inliers.
 /// </summary>
 /// <param name="points">Point Array - here the most dominant plane will be fitted</param>
 /// <param name="iterations">Number of RANSAC iterations: depends on the expected ratio between inliers and outliers. Independent of point array's length.</param>
 /// <param name="minConsensus">Minimum number of inlier points to be in the plane</param>
 /// <param name="epsilon">The +-distance of the points to the plane to be considered (absolute value)</param>
 /// <param name="plane">The found plane</param>
 /// <returns>returns true if a valid plane was found</returns>
 public static bool FitPlane3dRansac(this V3d[] points, int iterations, int minConsensus, double epsilon, out Plane3d plane)
 {
     return(FitPlane3dRansac(points, iterations, minConsensus, epsilon, out plane, out int[] inlier));
 }
예제 #28
0
 /// <summary>
 /// Count points approximately NOT within maxDistance of given plane.
 /// Result is always equal or greater than exact number.
 /// Faster than CountPointsNotNearPlane.
 /// </summary>
 public static long CountPointsApproximatelyNotNearPlane(
     this PointSet self, Plane3d plane, double maxDistance, int minCellExponent = int.MinValue
     )
 => CountPointsApproximatelyNotNearPlane(self.Root.Value, plane, maxDistance, minCellExponent);
예제 #29
0
        public static void Main()
        {
            // Examples of basic operations with GeometRi

            // Global coordinate system is created automatically and can be accessed as "Coord3d.GlobalCS"
            Console.WriteLine("Number of defined coordinate systems: {0}", Coord3d.Counts);
            Console.WriteLine();
            Console.WriteLine("Default coordinate system: ");
            Console.WriteLine(Coord3d.GlobalCS.ToString());



            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("!!! Find intersection of plane with line !!!");

            // Define point and vector in global CS
            Point3d  p1 = new Point3d(1, -5, -1);
            Vector3d v1 = new Vector3d(-2, 3, 4);

            // Define line using point and vector
            Line3d l1 = new Line3d(p1, v1);

            // Define plane using general equation in 3D space in the form "A*x+B*y+C*z+D=0"
            Plane3d s1 = new Plane3d(-2, 2, 3, -29);

            // Find the intersection of line with plane.
            // The results could be point, line or nothing, therefore get result as general object
            // and determine it's type.
            object obj = l1.IntersectionWith(s1);

            if (obj != null)
            {
                if (obj.GetType() == typeof(Line3d))
                {
                    Console.WriteLine("Intersection is line");
                    Line3d l2 = (Line3d)obj;
                    Console.WriteLine(l2.ToString());
                }
                else if (obj.GetType() == typeof(Point3d))
                {
                    Console.WriteLine("Intersection is point");
                    Point3d p2 = (Point3d)obj;
                    Console.WriteLine(p2.ToString());
                }
            }

            // Short variant
            // Will cause "InvalidCastException" if the intersection is not a point
            Point3d p3 = (Point3d)l1.IntersectionWith(s1);

            Console.WriteLine(p3.ToString());



            Line3d ll1 = new Line3d(new Point3d(2, 2, 2), new Vector3d(1, 1, 1));
            Line3d ll2 = new Line3d(new Point3d(201, 200, 200), new Vector3d(-10, -10, -10));

            GeometRi3D.Tolerance            = 0.01;
            GeometRi3D.UseAbsoluteTolerance = false;
            if (ll1.Equals(ll2))
            {
            }
            ;


            Console.ReadLine();
        }
예제 #30
0
 /// <summary>
 /// All points NOT within maxDistance of given plane.
 /// </summary>
 public static IEnumerable <Chunk> QueryPointsNotNearPlane(
     this PointSet self, Plane3d plane, double maxDistance, int minCellExponent = int.MinValue
     )
 => QueryPointsNotNearPlane(self.Root.Value, plane, maxDistance, minCellExponent);