Wall.Create from curves for non-rectangular profile creates wrong geometry

Wall.Create from curves for non-rectangular profile creates wrong geometry

boostyourbim
Advocate Advocate
2,463 Views
5 Replies
Message 1 of 6

Wall.Create from curves for non-rectangular profile creates wrong geometry

boostyourbim
Advocate
Advocate

 

I want to create a non-rectangular wall. This code uses 3 points to define 3 lines. But the wall that Revit creates does not match the lines. What is wrong?

 

        public void walltest()
        {
            Document doc = this.ActiveUIDocument.Document;
            
            // create points
            List<XYZ> pts = new List<XYZ>();
            XYZ first = new XYZ(0020);
            XYZ second = new XYZ(20035);
            XYZ third = new XYZ(30055);
                    
            // create curves connecting these points
            IList<Curve> profile = new List<Curve>();
            profile.Add(Line.CreateBound(first, second));
            profile.Add(Line.CreateBound(second, third));
            profile.Add(Line.CreateBound(third, first));
            
            // create model lines for each line (for diagnostics)
            foreach (Curve line in profile)
            {
                makeModelLine(doc, (Line)line);
            }
    
            // create wall;
            using (Transaction t = new Transaction(doc, "test"))
            {
                t.Start();
                Wall.Create(doc, profile, false);
                t.Commit();
            }
        }
        

// helper functions to create model lines

            public static ModelLine makeModelLine(Document doc, Line line)
        {
            if (line == null)
                return null;

            return makeLine(doc, line.GetEndPoint(0), line.GetEndPoint(1));
        }

           public static ModelLine makeLine(Document doc, XYZ pt1, XYZ pt2)
        {
            if (pt1 == null || pt2 == null)
                return null;

            ModelLine ret = null;
            using (Transaction t = new Transaction(doc, "g"))
            {
                
                bool started = false;
                try
                {
                    t.Start();
                    started = true;
                }
                catch
                { }

                ret = (ModelLine)doc.Create.NewModelCurve(Line.CreateBound(pt1, pt2), SketchPlane.Create(doc, makePlane(doc.Application, pt1, pt2)));

                if (started)
                    t.Commit();
            }
            return ret;
        }

        private static Plane makePlane(Autodesk.Revit.ApplicationServices.Application app, XYZ pt1, XYZ pt2)
        {
            XYZ v = pt1.Subtract(pt2);
            double dxy = Math.Abs(v.X) + Math.Abs(v.Y);
            XYZ w = (dxy > 0.0001) ? XYZ.BasisZ : XYZ.BasisY;
            XYZ norm = v.CrossProduct(w).Normalize();
            return app.Create.NewPlane(norm, pt1);
        }

 

 

0 Likes
Accepted solutions (1)
2,464 Views
5 Replies
Replies (5)
Message 2 of 6

Anonymous
Not applicable
Just a guess but it looks like the API is moving (Maybe even projecting?)the profile so that the lowest point is on the lowest (or current wall placement default) level. Maybe there's another Create() overload you can use or you could set wall parameters to remove the constraint? Like I said, just guesswork. What happens if you change the profile so that the lowest point is on level 1? Does the wall then match up?
0 Likes
Message 3 of 6

Anonymous
Not applicable

I made a few changes and I think it will now work as intended.

 

 

public void walltest()
{
	Document doc = this.ActiveUIDocument.Document;

	// create points
	List<XYZ> pts = new List<XYZ>();
	XYZ first = new XYZ(0, 0, 20);
	XYZ second = new XYZ(20, 0, 35);
	XYZ third = new XYZ(30, 0, 55);


	XYZ lowest = first;

	// create curves connecting these points
	IList<Curve> profile = new List<Curve>();
	profile.Add(Line.CreateBound(first, second));
	profile.Add(Line.CreateBound(second, third));
	profile.Add(Line.CreateBound(third, first));

	// create model lines for each line (for diagnostics)
	foreach(Curve line in profile)
	{
		makeModelLine(doc, (Line)line);
	}

	// create wall;
	using(Transaction t = new Transaction(doc, "test"))
	{
		t.Start();
		Wall newWall = Wall.Create(doc, profile, false);

		doc.Regenerate();

		Level wallLevel = doc.GetElement(newWall.LevelId) as Level;

		Double lowPointOffset = lowest.Z - wallLevel.Elevation;


		Parameter paramBaseOffset = newWall.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET);

		if(paramBaseOffset != null)
		{
			paramBaseOffset.Set(lowPointOffset);
		}

		t.Commit();
	}
}
0 Likes
Message 4 of 6

boostyourbim
Advocate
Advocate

Hi Scott,

 

Thanks for the suggestion, but in addition to the bottom offset problem there is also a problem with Revit changing the z-value of points to match a level above where the points should be.

 

For example, this code works properly in my RVT when the only level is T.O. FOOTING. But when both levels in the file exist, the top point of the triangle gets moved by Revit so that the shape of the triangle is stretched. Changing the bottom offset to correct the Z of the bottom points is not sufficient because Revit has altered the shape, not just shifted it.

 

RVT at https://drive.google.com/file/d/0BwszsfY3OsZHN1RnRHlTZ2pJWTA/view?usp=sharing

 

        public void makeWall()
        {
            Document doc = this.Document;
            XYZ pt1 = new XYZ(-509);
            XYZ pt2 = new XYZ(0010);
            XYZ pt3 = new XYZ(509);
            
            TaskDialog.Show("points",pt1.ToString() + Environment.NewLine 
                            + pt2.ToString() + Environment.NewLine 
                            + pt3.ToString() + Environment.NewLine);
            
            IList<Curve> curves = new List<Curve>();
            curves.Add(Line.CreateBound(pt1, pt2));
            curves.Add(Line.CreateBound(pt2, pt3));
            curves.Add(Line.CreateBound(pt3, pt1));
            
            makeWallProfile(doc, curves);
        }
        
        public Wall makeWallProfile(Document doc, IList<Curve> profile)
        {
            Wall newWall = null;
            using (Transaction t = new Transaction(doc, "gable"))
            {
                t.Start();
                newWall = Wall.Create(doc, profile, false);
                t.Commit();
            }
            return newWall;
        }

 
Message 5 of 6

Anonymous
Not applicable
Accepted solution

I didn't get that problem my end so I did some experimenting and it seems that Revit adopts the top constraint settings that were last used when a wall was created by a user. try creating a wall by explicitly setting it as unconnected before creation and then run your command again, it should be fine. I noticed that if you change the properties of the wall that was incorrectly created by the command, the geometry will go back to normal (set unconnected and height to 1ft) well it worked for this simple geometry anyway, not sure about more complex profiles.

 

I have no idea how to change the default UI wall creation settings (or even how to read them) so I made a few more changes to the code to come at it from the other end, detecting  the top constraint, removing it and then setting the correct wall height. It would be nice if there was a wall creation overload to explicitly set the top constraint instead of needing to check & repair whatever the API decides to give you...

 

public void walltest()
{
	Document doc = this.ActiveUIDocument.Document;

	// create points
	List<XYZ> pts = new List<XYZ>();
	XYZ first = new XYZ(-5, 0, 9);
	XYZ second = new XYZ(0, 0, 10);
	XYZ third = new XYZ(5, 0, 9);


	XYZ lowest = first;
	XYZ highest = second;

	// create curves connecting these points
	IList<Curve> profile = new List<Curve>();
	profile.Add(Line.CreateBound(first, second));
	profile.Add(Line.CreateBound(second, third));
	profile.Add(Line.CreateBound(third, first));

	// create model lines for each line (for diagnostics)
	foreach(Curve line in profile)
	{
		makeModelLine(doc, (Line)line);
	}

	// create wall;
	using(Transaction t = new Transaction(doc, "test"))
	{
		t.Start();
		Wall newWall = Wall.Create(doc, profile, false);

		doc.Regenerate();

		Level wallLevel = doc.GetElement(newWall.LevelId) as Level;

		Double lowPointOffset = lowest.Z - wallLevel.Elevation;

		


		Parameter paramBaseOffset = newWall.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET);

		Parameter paramTopConstraint = newWall.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE);

		Parameter paramWallHeight = newWall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM);
		



		if(paramBaseOffset != null)
		{
			paramBaseOffset.Set(lowPointOffset);
		}
		if(paramTopConstraint != null && paramWallHeight != null)
		{
			if(paramTopConstraint.AsElementId() != ElementId.InvalidElementId)
			{
				paramTopConstraint.Set(ElementId.InvalidElementId);
				paramWallHeight.Set(highest.Z - lowest.Z);
			}
			
			
		}


		t.Commit();
	}
}
Message 6 of 6

boostyourbim
Advocate
Advocate
Scott - Thank you! That workaround seems to do it!

Could someone from Autodesk confirm this bug and report it to the development team? At the very least, the CHM file should document this workaround.
0 Likes