Ejemplo n.º 1
0
        override public List <xyz> GetProfile(HalfEdge he)
        {
            List <xyz> pts = new List <xyz>();

            pts.Add(he.from.copy());
            pts.Add(he.from + he.GetInwardNormal() * delta1);
            pts.Add(he.from + he.GetInwardNormal() * delta1 + he.Opposite().GetInwardNormal() * delta2);
            pts.Add(he.from + he.Opposite().GetInwardNormal() * delta2);

            return(pts);
        }
Ejemplo n.º 2
0
        public static CompoundSolid Crosscut_Or_Rip(CompoundSolid sol, HalfEdge he, Inches distAlongEdge, double miter, double tilt)
        {
            Face f = he.face;

            // TODO fix these names
            string[] fnames = new string[] {
                "miter_top",
                "miter_right",
                "miter_left",
                "miter_bottom",
                "miter",                                // the primary face name which gets left
                "miter_below",
            };

            xyz nrml   = f.UnitNormal();
            xyz origin = he.to + he.GetInwardNormal() * distAlongEdge;

            // vx is in the direction of the cut
            xyz vx = ut.RotateUnitVector(-he.UnitVector(), -miter, nrml);

            // now rotate nrml for tilt
            nrml = ut.RotateUnitVector(nrml, tilt, vx);

            // origin is going to be the corner of the cutter box.
            // right now it is sitting on the face.  we need to
            // move it further away.  calculate intersections with
            // the boundingbox of the compoundsolid as a whole.

            BoundingBox3d bb     = sol.GetBoundingBox();
            double        d_vx   = bb.IntersectRay_Planes_Max(origin, -vx);
            double        d_nrml = bb.IntersectRay_Planes_Max(origin, nrml);

            // TODO this approach creates a bb which is too big.  do we care?

            // now move origin far enough back behind the face
            origin.subtract_in_place((d_vx + 2) * vx);
            // and up above the face
            origin = origin + (d_nrml + 2) * nrml;

            xyz vy = xyz.cross(nrml, vx).normalize_in_place();
            xyz vz = -nrml;

            d_vx = bb.IntersectRay_Planes_Max(origin, vx);
            double d_vy = bb.IntersectRay_Planes_Max(origin, vy);
            double d_vz = bb.IntersectRay_Planes_Max(origin, vz);

            // now calculate proper sizes for the cutter
            double dx = d_vx + 2;
            double dy = d_vy + 2;
            double dz = d_vz + 2;

            Solid cutter = CreateCutter_Box(string.Format("{0}_{1}", f.name, he.Opposite().face.name), origin, vx, nrml, new xyz(dx, dy, dz), fnames);

            //sol = sol.Clone();
            //sol.AddSub(cutter);
            //return sol;
            return(bool3d.Subtract(sol, cutter));
        }
Ejemplo n.º 3
0
        override public List <xyz> GetProfile(HalfEdge he)
        {
            List <xyz> pts = new List <xyz>();

            xyz p1 = he.from.copy();
            xyz p2 = he.from + he.GetInwardNormal() * delta;
            xyz p3 = he.from + he.Opposite().GetInwardNormal() * delta;

#if not
            xyz p1off = (-he.GetInwardNormal() - he.Opposite().GetInwardNormal()).normalize();
            xyz p2off = (p2 - p3).normalize();
            xyz p3off = -p2off;

            pts.Add(p1 + p1off);
            pts.Add(p2 + p2off);
            pts.Add(p3 + p3off);
#endif
            pts.Add(p1);
            pts.Add(p2);
            pts.Add(p3);

            return(pts);
        }
Ejemplo n.º 4
0
        override public List <xyz> GetProfile(HalfEdge he)
        {
            List <xyz> pts = new List <xyz>();

            pts.Add(he.from.copy());
            xyz center = he.from + he.GetInwardNormal() * radius + he.Opposite().GetInwardNormal() * radius;
            xyz xv     = -he.GetInwardNormal();
            xyz yv     = -he.Opposite().GetInwardNormal();

            const int steps = 7;

            for (int i = steps; i >= 0; i--)
            {
                double deg = i * 90.0 / steps;
                double rad = ut.DegreeToRadian(deg);
                double x   = radius * Math.Cos(rad);
                double y   = radius * Math.Sin(rad);
                xyz    pt  = center + x * xv + y * yv;
                pts.Add(pt);
            }

            return(pts);
        }
Ejemplo n.º 5
0
        private static void CreateFaceLabel(Viewport3D myVP, Face f, TranslateTransform3D ttmove)
        {
            HalfEdge he   = f.FindLongestEdge();
            xyz      p1   = he.Center();
            xyz      p2   = f.Measure(he, p1);
            xyz      v    = p2 - p1;
            double   dist = v.magnitude();
            xyz      c    = p1 + v / 2;

            c += f.UnitNormal() * .001;
            Point3D  center = wpfmisc.fixPoint(c);
            Vector3D over   = wpfmisc.fixVector(he.UnitVector());
            Vector3D up     = wpfmisc.fixVector(he.GetInwardNormal());

            double height;

            if (dist <= 1)
            {
                height = dist / 3;
            }
            else if (dist < 10)
            {
                height = 1;
            }
            else
            {
                height = 2;
            }

            // TODO the following isn't a very pretty hack
            if (f.name.Length * height > he.Length())
            {
                height = he.Length() / f.name.Length;
            }

            ModelVisual3D mv3d = wpfmisc.CreateTextLabel3D(f.name, Brushes.Black, false, height, center, over, up);

            if (ttmove != null)
            {
                mv3d.Transform = ttmove;
            }
            myVP.Children.Add(mv3d);
        }
Ejemplo n.º 6
0
        public static void Edges(IOrient s2, Face f1, Face f2, HalfEdge he1, HalfEdge he2, EdgeAlignment align, double offset1, double offset2, bool reversed)
        {
            xyz v1;
            xyz v2;

            xyz uv1 = (he1.to - he1.from).normalize_in_place();

            Debug.Assert(
                (align == EdgeAlignment.Center) ||
                (align == EdgeAlignment.Right) ||
                (align == EdgeAlignment.Left)
                );

            // move the solid to match up two points
            if (align == EdgeAlignment.Center)
            {
                v1 = he1.Center();
                if (!fp.eq_inches(offset1, 0))
                {
                    v1 += (offset1 * uv1);
                }
                v2 = he2.Center();
            }
            else if (align == EdgeAlignment.Right)
            {
                v1 = he1.to;
                if (!fp.eq_inches(offset1, 0))
                {
                    v1 -= (offset1 * uv1);
                }
                v2 = he2.from;
            }
            else // left
            {
                v1 = he1.from;
                if (!fp.eq_inches(offset1, 0))
                {
                    v1 += (offset1 * uv1);
                }
                v2 = he2.to;
            }

            if (!fp.eq_inches(offset2, 0))
            {
                xyz uv1perp = -(he1.GetInwardNormal());
                v1 += (offset2 * uv1perp);
            }

            xyz tv = v1 - v2;

            s2.Translate(tv.x, tv.y, tv.z);

            if (reversed)
            {
                RotateSoFacesAreParallelAndSameDir(s2, f1, f2, v1);
            }
            else
            {
                RotateSoFacesAreParallelAndOpposite(s2, f1, f2, v1);
            }

            // now rotate around origin = f1.MainLoop[0].to and vector = n1 to align the other points
            xyz q1 = he1.UnitVector();
            xyz q2 = he2.UnitVector();

            if (reversed)
            {
                q2 = -q2;
            }
            if (fp.eq_unitvec(q1, q2))
            {
                s2.Rotate(-1, 0, v1, f1.UnitNormal());
            }
            else if (fp.eq_unitvec(q1, -q2))
            {
                // do nothing.  this is perfect.
            }
            else
            {
                double dot = xyz.dot(q1, q2);
                xyz    kp  = xyz.cross(q2, q1).normalize_in_place();
                s2.Rotate(-dot, -Math.Sqrt(1 - dot * dot), v1, kp);
            }
        }
Ejemplo n.º 7
0
        internal void Execute()
        {
            Errors.Clear();
            Warnings.Clear();
            annotations_PP.Clear();
            facesToBeLabeled.Clear();
            prose = null;

            try
            {
                /*
                 * This switch statement must have one case for
                 * every kind of step action.
                 *
                 * Each case in this switch statement needs to:
                 *
                 * 1.  retrieve the parameters for the step
                 * 2.  do error checking on those parameters
                 * 3.  calculate the prose description of the step
                 * 4.  execute the step
                 * 5.  add faces to be labeled
                 * 6.  add annotations
                 * */

                switch (action)
                {
                case Action.INTRO:
                {
                    prose   = "";
                    _result = plan.LastStep.Result;
                    break;
                }

                case Action.DRILL:
                {
                    Inches x     = Get_Eval("x");
                    Inches y     = Get_Eval("y");
                    Inches diam  = Get_Eval("diam");
                    Inches depth = Get_Eval("depth");
                    string id    = Get_String("id");
                    Inches dx    = Get_Eval("dx");
                    Inches dy    = Get_Eval("dy");
                    int    count = Get_Eval_Integer("count");

                    CompoundSolid cs;
                    Solid         sol;
                    Face          f;
                    HalfEdge      he;
                    Lookup(Get_String("path"), out cs, out sol, out f, out he);

                    if (depth > Limits.MAX_DRILL_DEPTH)
                    {
                        Warnings.Add(string.Format("Drill depth is absurd"));
                    }

                    StringBuilder sb = new StringBuilder();
                    if (count > 1)
                    {
                        sb.AppendFormat("Drill {2} holes\r\n{0} in diameter and {1} deep\r\n", diam.GetProse(), depth.GetProse(), count);
                        sb.AppendFormat("in the '{0}' face of the board named '{1}'\r\n", f.name, sol.name);
                        sb.AppendFormat("\r\nstarting at {0}, {1} inches from\r\nthe edge between faces '{2}' and '{3}'.\r\n", x.GetStringWithoutUnits(), y.GetStringWithoutUnits(), f.name, he.Opposite().face.name);
                        // TODO what if dx and dy are both non-zero?
                        if (dx > 0)
                        {
                            sb.AppendFormat("{0} apart", dx.GetProse());
                        }
                        else
                        {
                            sb.AppendFormat("{0} apart", dy.GetProse());
                        }
                    }
                    else
                    {
                        sb.AppendFormat("Drill a hole\r\n{0} in diameter and {1} deep\r\n", diam.GetProse(), depth.GetProse());
                        sb.AppendFormat("in the '{0}' face of the board named '{1}'\r\n", f.name, sol.name);
                        sb.AppendFormat("\r\nat {0}, {1} inches from\r\nthe edge between faces '{2}' and '{3}'.\r\n", x.GetStringWithoutUnits(), y.GetStringWithoutUnits(), f.name, he.Opposite().face.name);
                    }
                    prose = sb.ToString();

                    _result = wood.Drill(cs, he, x, y, count, dx, dy, diam, depth, id);

                    facesToBeLabeled.Add(_result.FindFace(sol.name, f.name));

                    break;
                }

                case Action.TENON:
                {
                    Inches x     = Get_Eval("x");
                    Inches y     = Get_Eval("y");
                    Inches xsize = Get_Eval("xsize");
                    Inches ysize = Get_Eval("ysize");
                    Inches depth = Get_Eval("depth");
                    string id    = Get_String("id");

                    CompoundSolid cs;
                    Solid         sol;
                    Face          f;
                    HalfEdge      he;
                    Lookup(Get_String("path"), out cs, out sol, out f, out he);

                    StringBuilder sb = new StringBuilder();
                    sb.AppendFormat("Cut a tenon\r\n{0} by {1}\r\n{2} deep\r\n", xsize.GetProse(), ysize.GetProse(), depth.GetProse());
                    sb.AppendFormat("in the '{0}' face of the board named '{1}'\r\n", f.name, sol.name);
                    sb.AppendFormat("at {0}, {1} inches from\r\nthe edge between faces '{2}' and '{3}'.\r\n", x.GetStringWithoutUnits(), y.GetStringWithoutUnits(), f.name, he.Opposite().face.name);
                    sb.AppendFormat("Call this tenon '{0}'.", id);
                    prose = sb.ToString();

                    _result = wood.Tenon(cs, he, new xy(x, y), new xyz(xsize, ysize, depth), id);

                    string _s, _f, _e;
                    ut.ParsePath(Get_String("path"), out _s, out _f, out _e);

                    Face newendface = _result.FindFace(_s, _f);
                    if (newendface != null)
                    {
                        facesToBeLabeled.Add(newendface);

                        HalfEdge heother = newendface.FindEdge(_e);
                        if (heother != null)
                        {
                            facesToBeLabeled.Add(heother.Opposite().face);
                        }

                        HalfEdge hea = newendface.FindEdge(string.Format("{0}_left", id));
                        if (hea == null)
                        {
                            hea = newendface.FindEdge(string.Format("{0}_right", id));
                        }
                        if (hea != null)
                        {
                            this.annotations_PP.Add(new Annotation_PointToPoint(hea.from, hea.to, newendface.UnitNormal(), 2));
                        }

                        hea = newendface.FindEdge(string.Format("{0}_front", id));
                        if (hea == null)
                        {
                            hea = newendface.FindEdge(string.Format("{0}_back", id));
                        }
                        if (hea != null)
                        {
                            this.annotations_PP.Add(new Annotation_PointToPoint(hea.from, hea.to, newendface.UnitNormal(), 2));
                        }
                    }

                    // TODO more annotations here for the dimensions of the shoulders?

                    break;
                }

                case Action.MORTISE:
                {
                    Inches x     = Get_Eval("x");
                    Inches y     = Get_Eval("y");
                    Inches xsize = Get_Eval("xsize");
                    Inches ysize = Get_Eval("ysize");
                    Inches depth = Get_Eval("depth");
                    string id    = Get_String("id");
                    Inches dx    = Get_Eval("dx");
                    Inches dy    = Get_Eval("dy");
                    int    count = Get_Eval_Integer("count");

                    CompoundSolid cs;
                    Solid         sol;
                    Face          f;
                    HalfEdge      he;
                    Lookup(Get_String("path"), out cs, out sol, out f, out he);

                    if (depth > Limits.MAX_MORTISE_DEPTH)
                    {
                        Warnings.Add(string.Format("Mortise depth is absurd"));
                    }

                    StringBuilder sb = new StringBuilder();
                    if (count == 1)
                    {
                        sb.AppendFormat("Cut a mortise\r\n{0} by {1}\r\n{2} deep\r\n", xsize.GetProse(), ysize.GetProse(), depth.GetProse());
                        sb.AppendFormat("in the '{0}' face of the board named '{1}'\r\n", f.name, sol.name);
                        sb.AppendFormat("at {0}, {1} inches from\r\nthe edge between faces '{2}' and '{3}'.\r\n", x.GetStringWithoutUnits(), y.GetStringWithoutUnits(), f.name, he.Opposite().face.name);
                        //sb.AppendFormat("Call this mortise '{0}'.", id);
                    }
                    else
                    {
                        sb.AppendFormat("Cut {3} mortises\r\n{0} by {1}\r\n{2} deep\r\n", xsize.GetProse(), ysize.GetProse(), depth.GetProse(), count);
                        sb.AppendFormat("in the '{0}' face of the board named '{1}'\r\n", f.name, sol.name);
                        sb.AppendFormat("starting at {0}, {1} inches from\r\nthe edge between faces '{2}' and '{3}'\r\n", x.GetStringWithoutUnits(), y.GetStringWithoutUnits(), f.name, he.Opposite().face.name);
                        // TODO what if dx and dy are both non-zero?
                        if (dx > 0)
                        {
                            sb.AppendFormat("{0} apart", dx.GetProse());
                        }
                        else
                        {
                            sb.AppendFormat("{0} apart", dy.GetProse());
                        }
                        //sb.AppendFormat("Call this mortise '{0}'.", id);
                    }
                    prose   = sb.ToString();
                    _result = wood.Mortise(cs, he, new xy(x, y), new xyz(xsize, ysize, depth), count, dx, dy, id);

                    string _s, _f, _e;
                    ut.ParsePath(Get_String("path"), out _s, out _f, out _e);
                    Face newf = _result.FindFace(_s, _f);
                    if (newf != null)
                    {
                        facesToBeLabeled.Add(newf);
                        HalfEdge heother = newf.FindEdge(_e);
                        if (heother != null)
                        {
                            facesToBeLabeled.Add(heother.Opposite().face);
                        }
                    }

                    if (count == 1)
                    {
                        // label the dimensions of the mortise.  first across the front or back
                        HalfEdge hea = _result.FindEdge(sol.name, string.Format("{0}_front", id), f.name);
                        if (hea == null)
                        {
                            hea = _result.FindEdge(sol.name, string.Format("{0}_back", id), f.name);
                        }
                        if (hea != null)
                        {
                            this.annotations_PP.Add(new Annotation_PointToPoint(hea.from, hea.to, f.UnitNormal(), 2));
                        }

                        // now along the left or right
                        hea = _result.FindEdge(sol.name, string.Format("{0}_left", id), f.name);
                        if (hea == null)
                        {
                            hea = _result.FindEdge(sol.name, string.Format("{0}_right", id), f.name);
                        }
                        if (hea != null)
                        {
                            this.annotations_PP.Add(new Annotation_PointToPoint(hea.from, hea.to, f.UnitNormal(), 2));
                        }

                        // now label the location of the mortise
                        if (newf != null)
                        {
                            hea = newf.FindEdge(string.Format("{0}_front", id));

                            if (hea != null)
                            {
                                xyz p1 = hea.from;
                                xyz p2 = newf.Measure(hea, p1);
                                if (p2 != null)
                                {
                                    this.annotations_PP.Add(new Annotation_PointToPoint(p1, p2, newf.UnitNormal(), 2));
                                }
                            }

                            hea = newf.FindEdge(string.Format("{0}_right", id));
                            if (hea != null)
                            {
                                xyz p1 = hea.from;
                                xyz p2 = newf.Measure(hea, p1);
                                if (p2 != null)
                                {
                                    this.annotations_PP.Add(new Annotation_PointToPoint(p1, p2, newf.UnitNormal(), 2));
                                }
                            }
                        }
                    }

                    break;
                }

                case Action.DADO:
                {
                    Inches dist  = Get_Eval("dist");
                    Inches width = Get_Eval("width");
                    Inches depth = Get_Eval("depth");
                    string path  = Get_String("path");
                    string id    = Get_String("id");

                    CompoundSolid cs;
                    Solid         sol;
                    Face          f;
                    HalfEdge      he;
                    Lookup(path, out cs, out sol, out f, out he);

                    StringBuilder sb = new StringBuilder();
                    sb.AppendFormat("Cut a dado\r\n{0} wide and {1} deep\r\n", width.GetProse(), depth.GetProse());
                    sb.AppendFormat("in the '{0}' face of the board named '{1}'\r\n", f.name, sol.name);
                    sb.AppendFormat("parallel to and {0} from the edge between faces '{1}' and '{2}'.\r\n", dist.GetProse(), f.name, he.Opposite().face.name);
                    sb.AppendFormat("Call this dado '{0}'.", id);
                    prose = sb.ToString();

                    _result = wood.Dado(cs, he, dist, width, depth, id);

                    facesToBeLabeled.Add(_result.FindFace(string.Format("{0}.{1}_1", sol.name, f.name)));
                    facesToBeLabeled.Add(_result.FindFace(string.Format("{0}.{1}_2", sol.name, f.name)));

                    break;
                }

                case Action.RIP:
                {
                    Inches dist  = Get_Eval("dist");
                    double taper = Get_Eval_Angle("taper");
                    double tilt  = Get_Eval_Angle("tilt");

                    CompoundSolid cs;
                    Solid         sol;
                    Face          f;
                    HalfEdge      he;
                    Lookup(Get_String("path"), out cs, out sol, out f, out he);

                    if (he.Opposite().face.GetQuality() == FaceQuality.EndGrain)          // TODO is this how we want this to work?
                    {
                        Warnings.Add("This is not really a rip.");
                    }

                    StringBuilder sb = new StringBuilder();
                    sb.AppendFormat("TODO");
                    prose = sb.ToString();

                    _result = wood.Crosscut_Or_Rip(cs, he, dist, taper, tilt);

                    facesToBeLabeled.Add(_result.FindFace(sol.name, f.name));
                    facesToBeLabeled.Add(_result.FindFace(sol.name, he.Opposite().face.name));

                    break;
                }

                case Action.CROSSCUT:
                {
                    Inches dist  = Get_Eval("dist");
                    double miter = Get_Eval_Angle("miter");
                    double tilt  = Get_Eval_Angle("tilt");

                    CompoundSolid cs;
                    Solid         sol;
                    Face          f;
                    HalfEdge      he;
                    Lookup(Get_String("path"), out cs, out sol, out f, out he);

                    if (he.Opposite().face.GetQuality() != FaceQuality.EndGrain)         // TODO is this how we want this to work?
                    {
                        Warnings.Add("This is not really a crosscut.");
                    }

                    StringBuilder sb = new StringBuilder();
                    sb.AppendFormat("TODO");
                    prose = sb.ToString();

                    _result = wood.Crosscut_Or_Rip(cs, he, dist, miter, tilt);

                    facesToBeLabeled.Add(_result.FindFace(sol.name, f.name));
                    facesToBeLabeled.Add(_result.FindFace(sol.name, he.Opposite().face.name));

                    break;
                }

                case Action.CHAMFER:
                {
                    Inches inset = Get_Eval("inset");
                    string id    = Get_String("id");

                    CompoundSolid cs;
                    Solid         sol;
                    Face          f;
                    HalfEdge      he;
                    Lookup(Get_String("path"), out cs, out sol, out f, out he);

                    StringBuilder sb = new StringBuilder();
                    sb.AppendFormat("Chamfer\r\nthe edge between the faces '{0}' and '{1}'\r\non the board '{2}'\r\n", f.name, he.Opposite().face.name, sol.name);
                    sb.AppendFormat("at a depth of {0}.", inset.GetProse());
                    prose = sb.ToString();

                    _result = wood.DoChamfer(cs, he, inset, id);

                    facesToBeLabeled.Add(_result.FindFace(sol.name, f.name));
                    facesToBeLabeled.Add(_result.FindFace(sol.name, he.Opposite().face.name));

                    xyz p1 = he.Center();

                    Face     chamface = _result.FindFace(sol.name, string.Format("{0}_2", id));
                    HalfEdge he1      = chamface.FindEdge(f.name);

                    this.annotations_PP.Add(new Annotation_PointToPoint(p1, he1.Center(), he1.Opposite().face.UnitNormal(), 2));

                    he1 = chamface.FindEdge(he.Opposite().face.name);
                    this.annotations_PP.Add(new Annotation_PointToPoint(p1, he1.Center(), he1.Opposite().face.UnitNormal(), 2));

                    break;
                }

                case Action.RABBET:
                {
                    Inches inset = Get_Eval("inset");
                    Inches depth = Get_Eval("depth");
                    string id    = Get_String("id");

                    CompoundSolid cs;
                    Solid         sol;
                    Face          f;
                    HalfEdge      he;
                    Lookup(Get_String("path"), out cs, out sol, out f, out he);

                    StringBuilder sb = new StringBuilder();
                    sb.AppendFormat("TODO");
                    prose = sb.ToString();

                    _result = wood.DoRabbet(cs, he, inset, depth, id);

                    facesToBeLabeled.Add(_result.FindFace(sol.name, f.name));
                    facesToBeLabeled.Add(_result.FindFace(sol.name, he.Opposite().face.name));

                    break;
                }

                case Action.ROUNDOVER:
                {
                    Inches radius = Get_Eval("radius");
                    string id     = Get_String("id");

                    CompoundSolid cs;
                    Solid         sol;
                    Face          f;
                    HalfEdge      he;
                    Lookup(Get_String("path"), out cs, out sol, out f, out he);

                    StringBuilder sb = new StringBuilder();
                    sb.AppendFormat("TODO");
                    prose = sb.ToString();

                    _result = wood.DoRoundover(cs, he, radius, id);

                    facesToBeLabeled.Add(_result.FindFace(sol.name, f.name));
                    facesToBeLabeled.Add(_result.FindFace(sol.name, he.Opposite().face.name));

                    break;
                }

                case Action.NEW_BOARD:
                {
                    Inches        width     = Get_Eval("width");
                    Inches        length    = Get_Eval("length");
                    Inches        thickness = Get_Eval("thickness");
                    string        newname   = Get_String("newname");
                    string        material  = Get_String("material");
                    BoardMaterial bm        = BoardMaterial.Find(material);

                    StringBuilder sb = new StringBuilder();
                    sb.AppendFormat("Cut a new board made of {0}\r\n", bm.GetProse());
                    sb.AppendFormat("{0} wide\r\n{1} long\r\n{2} thick\r\n", width.GetProse(), length.GetProse(), thickness.GetProse());
                    sb.AppendFormat("Call this board '{0}'.", newname);
                    prose = sb.ToString();

                    _result = wood.CreateBoard(bm, newname, width, length, thickness).ToCompoundSolid();

                    Face     ftop = _result.Subs[0].FindFace("top");
                    HalfEdge he   = ftop.FindEdge("end1");
                    this.annotations_PP.Add(new Annotation_PointToPoint(he.from, he.to, ftop.UnitNormal(), 2));

                    he = ftop.FindEdge("left");
                    this.annotations_PP.Add(new Annotation_PointToPoint(he.from, he.to, ftop.UnitNormal(), 2));

                    Face fend1 = _result.Subs[0].FindFace("end1");
                    he = fend1.FindEdge("left");
                    this.annotations_PP.Add(new Annotation_PointToPoint(he.from, he.to, fend1.UnitNormal(), 2));

                    this.facesToBeLabeled.AddRange(_result.Subs[0].Faces);

                    break;
                }

                case Action.JOIN:
                {
                    Inches        offset1 = Get_Eval("offset1");
                    Inches        offset2 = Get_Eval("offset2");
                    string        path1   = Get_String("path1");
                    string        path2   = Get_String("path2");
                    EdgeAlignment align   = Get_EdgeAlignment("align");

                    CompoundSolid s1;
                    CompoundSolid s2;
                    Solid         sol1;
                    Solid         sol2;
                    Face          f1;
                    Face          f2;
                    HalfEdge      he1;
                    HalfEdge      he2;

                    Lookup(path1, out s1);
                    Lookup(path2, out s2);

                    CompoundSolid snew = s1.ShallowClone();             // s1 doesn't get modified by the join, so we don't need full clone
                    CompoundSolid sadd = s2.Clone();                    // s2 gets rotated and moved, so this step needs its own copy of every subsolid

                    snew.Lookup(path1, out sol1, out f1, out he1);
                    sadd.Lookup(path2, out sol2, out f2, out he2);

                    StringBuilder sb = new StringBuilder();
                    sb.AppendFormat("Join the board named '{0}'\r\nto the board named '{1}'\r\nas follows:\r\n",
                                    sol2.name,
                                    sol1.name);
                    sb.AppendFormat("On '{0}' find the face '{1}'\r\nand the edge shared between that face and '{2}'.\r\n",
                                    sol2.name, f2.name, he2.Opposite().face.name);
                    sb.AppendFormat("On '{0}' find the face '{1}'\r\nand the edge shared between that face and '{2}'.\r\n",
                                    sol1.name, f1.name, he1.Opposite().face.name);
                    sb.AppendFormat("Join these two faces together by matching up the two edges described above");
                    // TODO align
                    if (offset1 > 0)
                    {
                        sb.AppendFormat("at an offset of {0} along the edges", offset1.GetProse());
                    }
                    if (offset2 > 0)
                    {
                        sb.AppendFormat("at an offset of {0} perpendicular to the edges", offset2.GetProse());
                    }
                    prose = sb.ToString();

                    orient.Edges(sadd, f1, f2, he1, he2, align, offset1, offset2);
                    snew.AddSub(sadd);

#if DEBUG
                    snew.AssertNoNameClashes();
#endif

                    if (!snew.IsValidWithNoSubOverlaps())
                    {
                        Errors.Add("Invalid join:  Two boards cannot occupy the same space.");
                    }

                    _result = snew;

                    facesToBeLabeled.Add(_result.FindFace(sol1.name, f1.name));
                    facesToBeLabeled.Add(_result.FindFace(sol1.name, he1.Opposite().face.name));

                    facesToBeLabeled.Add(_result.FindFace(sol2.name, f2.name));
                    facesToBeLabeled.Add(_result.FindFace(sol2.name, he2.Opposite().face.name));

                    break;
                }

                case Action.JOIN_MT:
                {
                    string path1 = Get_String("mortisepath");
                    string path2 = Get_String("tenonpath");

                    CompoundSolid s1;
                    CompoundSolid s2;
                    Solid         sol1;
                    Solid         sol2;
                    Face          f1;
                    Face          f2;
                    HalfEdge      he1;
                    HalfEdge      he2;

                    Lookup(path1, out s1);
                    Lookup(path2, out s2);

                    CompoundSolid snew = s1.ShallowClone();             // s1 doesn't get modified by the join, so we don't need full clone
                    CompoundSolid sadd = s2.Clone();                    // s2 gets rotated and moved, so this step needs its own copy of every subsolid

                    snew.Lookup(path1, out sol1, out f1, out he1);
                    sadd.Lookup(path2, out sol2, out f2, out he2);

                    StringBuilder sb = new StringBuilder();
                    sb.AppendFormat("Assemble a mortise/tenon joint\r\njoining the board named '{0}' to the board named '{1}' as follows:\r\n",
                                    sol2.name,
                                    sol1.name);
                    sb.AppendFormat(" On '{0}' find the face '{1}' and the edge shared between that face and '{2}'.",
                                    sol2.name, f2.name, he2.Opposite().face.name);
                    sb.AppendFormat(" On '{0}' find the face '{1}' and the edge shared between that face and '{2}'.",
                                    sol1.name, f1.name, he1.Opposite().face.name);
                    sb.AppendFormat(" Join these two faces together by matching up the two edges described above.");
                    prose = sb.ToString();

                    orient.Edges(sadd, f1, f2, he1, he2, EdgeAlignment.Center, 0, 0);
                    snew.AddSub(sadd);

                    // TODO verify that the gluejoint has at least 3 pairs of faces (but what about face pairs not-local to this joint?)
#if DEBUG
                    snew.AssertNoNameClashes();
#endif

                    if (!snew.IsValidWithNoSubOverlaps())
                    {
                        Errors.Add("Invalid join:  Two boards cannot occupy the same space.");
                    }

                    _result = snew;

                    facesToBeLabeled.Add(_result.FindFace(sol1.name, f1.name));
                    facesToBeLabeled.Add(_result.FindFace(sol1.name, he1.Opposite().face.name));

                    facesToBeLabeled.Add(_result.FindFace(sol2.name, f2.name));
                    facesToBeLabeled.Add(_result.FindFace(sol2.name, he2.Opposite().face.name));

                    break;
                }

                case Action.DOVETAIL_JOIN:
                {
                    string id = Get_String("id");
                    if (!plan.Dovetails.ContainsKey(id))
                    {
                        Errors.Add(string.Format("Dovetail not found: {0}", id));
                    }
                    else
                    {
                        Dovetail dt = plan.Dovetails[id];

                        CompoundSolid cs1;
                        CompoundSolid cs2;

                        Lookup(dt.path1, out cs1);
                        Lookup(dt.path2, out cs2);

                        CompoundSolid snew = cs1.Clone();
                        CompoundSolid sadd = cs2.Clone();

                        StringBuilder sb = new StringBuilder();
                        sb.AppendFormat("Assemble the dovetail joint called '{0}'\r\n",
                                        id);
                        prose = sb.ToString();

                        dt.Join(snew, sadd);

                        if (!snew.IsValidWithNoSubOverlaps())
                        {
                            Errors.Add("Invalid join:  Two boards cannot occupy the same space.");
                        }

                        _result = snew;

                        string _s1, _f1, _e1;
                        string _s2, _f2, _e2;
                        ut.ParsePath(dt.path1, out _s1, out _f1, out _e1);
                        ut.ParsePath(dt.path2, out _s2, out _f2, out _e2);

                        facesToBeLabeled.Add(_result.FindFace(_s1, _e1));
                        facesToBeLabeled.Add(_result.FindFace(_s2, _e2));
                    }
                    break;
                }

                case Action.DOVETAIL_PINS:
                {
                    string id = Get_String("id");
                    if (!plan.Dovetails.ContainsKey(id))
                    {
                        Errors.Add(string.Format("Dovetail not found: {0}", id));
                    }
                    else
                    {
                        Dovetail      dt = plan.Dovetails[id];
                        CompoundSolid cs1;
                        CompoundSolid cs2;

                        Lookup(dt.path1, out cs1);
                        Lookup(dt.path2, out cs2);

                        string s_s1, s_f1, s_e1;
                        ut.ParsePath(dt.path1, out s_s1, out s_f1, out s_e1);

                        StringBuilder sb = new StringBuilder();
                        sb.AppendFormat("For the dovetail joint called '{0}',\r\nCut the pins in the board named '{1}'\r\n",
                                        id,
                                        s_s1);
                        sb.AppendFormat("in the face named '{0}'\r\n", s_f1);
                        sb.AppendFormat("with the face named '{0}' to be the outside of the joint.", s_e1);
                        prose = sb.ToString();

                        _result = dt.Pins(cs1);

                        facesToBeLabeled.Add(_result.FindFace(s_s1, s_e1));
                    }
                    break;
                }

                case Action.DOVETAIL_TAILS:
                {
                    string id        = Get_String("id");
                    string path1     = Get_String("path1");
                    string path2     = Get_String("path2");
                    int    numtails  = Get_Eval_Integer("numtails");
                    Inches tailwidth = Get_Eval("tailwidth");
                    // TODO angle/slope

                    CompoundSolid cs1;
                    CompoundSolid cs2;
                    Solid         s1;
                    Solid         s2;
                    Face          f1;
                    Face          f2;
                    HalfEdge      he1;
                    HalfEdge      he2;

                    Lookup(Get_String("path1"), out cs1, out s1, out f1, out he1);
                    Lookup(Get_String("path2"), out cs2, out s2, out f2, out he2);

                    if (he1.face.GetQuality() != FaceQuality.EndGrain)
                    {
                        Warnings.Add("The face/edge for the tails should be endgrain");
                    }
                    if (he2.face.GetQuality() != FaceQuality.EndGrain)
                    {
                        Warnings.Add("The face/edge for the pins should be endgrain");
                    }

                    StringBuilder sb = new StringBuilder();
                    sb.AppendFormat("Begin a dovetail joint called '{0}'.\r\nCut the tails in the board named '{1}'\r\n",
                                    id,
                                    s2.name);
                    sb.AppendFormat("in the face named '{0}'\r\n", f1.name);
                    sb.AppendFormat("with the face named '{0}' to be the outside of the joint.", he1.Opposite().face.name);
                    prose = sb.ToString();

                    Dovetail dt = new Dovetail(id, path1, path2, numtails, tailwidth, cs1, s1, f1, he1, cs2, s2, f2, he2);

                    CompoundSolid newcs2 = dt.Tails(cs2);

                    this.plan.Dovetails[id] = dt;

#if DEBUG
                    newcs2.AssertNoNameClashes();
#endif

                    if (!newcs2.IsValidWithNoSubOverlaps())
                    {
                        Errors.Add("Invalid join:  Two boards cannot occupy the same space.");
                    }

                    _result = newcs2;

                    facesToBeLabeled.Add(_result.FindFace(s2.name, he2.Opposite().face.name));

                    break;
                }
                }
                Debug.Assert(prose != null);
            }
            catch (Exception e)
            {
                this.Errors.Add(e.Message);
            }

            if (_result != null)
            {
                foreach (Annotation_FaceToFace a in annotations_FF)
                {
                    Solid    s1;
                    Face     f1;
                    HalfEdge he1;

                    Solid    s2;
                    Face     f2;
                    HalfEdge he2;

                    _result.FindPath(a.path1, out s1, out f1, out he1);
                    _result.FindPath(a.path2, out s2, out f2, out he2);

                    xyz p1 = he1.Center();

                    xyz n  = f2.myPlane.n;
                    xyz p0 = f2.myPlane.pts[0];

                    xyz ptfar = p1 - f1.UnitNormal() * 1000;

                    double u  = xyz.dotsub(n, p0, p1) / xyz.dotsub(n, ptfar, p1);
                    xyz    p2 = (ptfar - p1).multiply_in_place(u).add_in_place(p1);

                    this.annotations_PP.Add(new Annotation_PointToPoint(p1, p2, -he1.GetInwardNormal(), a.offset, a.size));
                }
            }
        }
Ejemplo n.º 8
0
        public static void PartitionFace_CalcFaceIntersections(List <seg3d> segs, Face f1, Solid s2, bool reverse)
        {
            xyz n1 = f1.UnitNormal();

            for (int ndx_f2 = 0; ndx_f2 < s2.Faces.Count; ndx_f2++)
            {
                Face f2 = s2.Faces[ndx_f2];
                xyz  n2 = f2.UnitNormal();
                if (
                    fp.eq_unitvec(n1, n2) ||
                    fp.eq_unitvec(n1, -n2)
                    )
                {
                    continue;
                }

                if (!f1.MightIntersect_BB(f2))
                {
                    continue;
                }

                List <seg3d> isegs = ppi3d.CalcIntersection_NotCoplanar(f1, f2);
                if (
                    (isegs == null) ||
                    (isegs.Count == 0)
                    )
                {
                    continue;
                }

                foreach (seg3d s in isegs)
                {
                    FixSegmentDirection(s, f1, f2, reverse);
                }

                if (isegs.Count > 0)
                {
                    foreach (seg3d snew in isegs)
                    {
                        HalfEdge he1;
                        HalfEdge he2a;
                        bool     bIsEdgeOnFace1 = f1.IsAnEdge(snew, out he1);
                        bool     bIsEdgeOnFace2 = f2.IsAnEdge(snew, out he2a);

                        if (bIsEdgeOnFace1 && bIsEdgeOnFace2)
                        {
#if false // TODO This code seems like it should be right, but it causes problems.
                            //if (reverse)
                            {
                                HalfEdge he2b = he2a.Opposite();
                                xyz      ne1  = he1.GetInwardNormal();
                                xyz      n2a  = he2a.GetInwardNormal();
                                xyz      n2b  = he2b.GetInwardNormal();

                                if (bool3d.SplitsVEP(ne1, n2a, n2b, he2a.face.UnitNormal(), he2b.face.UnitNormal()))
                                {
                                    segs_Remove(segs, snew.a, snew.b);
                                }
                            }
#endif
                        }
                        else if (bIsEdgeOnFace1 && !bIsEdgeOnFace2)
                        {
                            // TODO we don't need this, right?
                        }
                        else if (!bIsEdgeOnFace1 && bIsEdgeOnFace2)
                        {
                            HalfEdge he2b  = he2a.Opposite();
                            xyz      norm1 = xyz.cross(n1, (snew.b - snew.a).normalize_in_place()).normalize_in_place();
                            xyz      n2a   = he2a.GetInwardNormal();
                            xyz      n2b   = he2b.GetInwardNormal();
                            if (bool3d.SplitsVEP(norm1, n2a, n2b, he2a.face.UnitNormal(), he2b.face.UnitNormal()))
                            {
                                segs_Add(segs, snew);
                            }
                            else if (bool3d.SplitsVEP(-norm1, n2a, n2b, he2a.face.UnitNormal(), he2b.face.UnitNormal()))
                            {
                                segs_Add(segs, snew);
                            }
                            else
                            {
                                // TODO do anything here?
                            }
                        }
                        else
                        {
                            segs_Add(segs, snew);
                        }
                    }
                }
                else
                {
                    // these two faces intersect only in points.  ignore this for now, but Hoffman says we need to analyze further.
                }
            }
        }
Ejemplo n.º 9
0
        public static void PartitionFace_HandleEdges(List <seg3d> segs, Face f1, Solid s2, bsp3d bsp2, bool reverse)
        {
            List <seg3d> rm = new List <seg3d>();

            segs.ForEach(delegate(seg3d s)
            {
                HalfEdge he1a;
                if (f1.IsAnEdge(s, out he1a))
                {
                    HalfEdge he1b = he1a.Opposite();
                    for (int ndx_f2 = 0; ndx_f2 < s2.Faces.Count; ndx_f2++)
                    {
                        Face f2       = s2.Faces[ndx_f2];
                        HalfEdge he2a = null;
                        if (f2.IsAnEdge(s, out he2a))
                        {
                            HalfEdge he2b = he2a.Opposite();

                            bool bkeep;

                            xyz n1  = he1a.GetInwardNormal();
                            xyz n2a = he2a.GetInwardNormal();
                            xyz n2b = he2b.GetInwardNormal();

                            bkeep = bool3d.SplitsVEP(n1, n2a, n2b, he2a.face.UnitNormal(), he2b.face.UnitNormal());

                            if (reverse)
                            {
                            }
                            else
                            {
                                bkeep = !bkeep;
                            }

                            if (!bkeep)
                            {
                                rm.Add(s);
                            }
                            break;
                        }

                        PointFaceIntersection pfi_a;
                        PointFaceIntersection pfi_b;
                        if (f2.CalcSegmentFaceIntersection_SamePlane(s, out pfi_a, out pfi_b))
                        {
                            bool bProceed = false;

                            if (
                                (
                                    (pfi_a == PointFaceIntersection.Inside) &&
                                    (pfi_b == PointFaceIntersection.Inside)
                                )
                                ||
                                (
                                    (pfi_a == PointFaceIntersection.Inside) &&
                                    (pfi_b == PointFaceIntersection.OnEdge)
                                )
                                ||
                                (
                                    (pfi_a == PointFaceIntersection.OnEdge) &&
                                    (pfi_b == PointFaceIntersection.Inside)
                                )
                                )
                            {
                                bProceed = true;
                            }
                            else if (
                                (
                                    (pfi_a == PointFaceIntersection.OnEdge) &&
                                    (pfi_b == PointFaceIntersection.OnEdge)
                                )
                                )
                            {
                                if (f2.solid.SegmentOnSurface(bsp2, s.a, s.b))
                                {
                                    bProceed = true;
                                }
                            }

                            if (bProceed)
                            {
                                bool bkeep;

                                double dot = xyz.dot(he1a.GetInwardNormal(), f2.UnitNormal());

                                if (fp.eq_dot_unit(dot, 0))
                                {
                                    // this occurs on the outside of f2
                                    bkeep = true;
                                }
                                else if (dot < 0)
                                {
                                    // this intersect occurs on the inside of f2
                                    bkeep = false;
                                }
                                else
                                {
                                    // this occurs on the outside of f2
                                    bkeep = true;
                                }

                                if (reverse)
                                {
                                    bkeep = !bkeep;
                                }

                                if (!bkeep)
                                {
                                    rm.Add(s);
                                    if (rm.Count == segs.Count)
                                    {
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
                else
                {
#if false // so far, it looks like we don't need this code.  It was written when CreateTenonInFaceWithHoleBool3dBug turned up.  I rewrote tenon to use the 4-cut method again and the bug "went away".  :-)
                    foreach (Face f2 in s2.Faces)
                    {
                        HalfEdge he2a = null;
                        if (f2.IsAnEdge(s, out he2a))
                        {
                            HalfEdge he2b = he2a.Opposite();

                            bool bkeep;

                            xyz n1 = xyz.cross((s.b - s.a).normalize_in_place(), f1.UnitNormal()).normalize_in_place();

                            xyz n2a = he2a.GetInwardNormal();
                            xyz n2b = he2b.GetInwardNormal();

                            if (bool3d.SplitsVEP(n1, n2a, n2b, he2a.face.UnitNormal(), he2b.face.UnitNormal()))
                            {
                                bkeep = true;
                            }
                            else if (bool3d.SplitsVEP(-n1, n2a, n2b, he2a.face.UnitNormal(), he2b.face.UnitNormal()))
                            {
                                bkeep = true;
                            }
                            else
                            {
                                bkeep = false;
                            }

                            if (reverse)
                            {
                            }
                            else
                            {
                                bkeep = !bkeep;
                            }

                            if (!bkeep)
                            {
                                rm.Add(s);
                            }
                            break;
                        }
                    }
#endif
                }
            }
                         );
            foreach (seg3d s in rm)
            {
                segs.Remove(s);
            }
        }