Author Topic: How to assign a double to a LineSegment3d? or write a custom wall class?  (Read 6341 times)

0 Members and 1 Guest are viewing this topic.

waterharbin

  • Guest
Hi.
I use a LineSegment3d to present a wall when I draw the skeleton of a house. But a wall has it own thickness, while a LineSegment3d doesn't. So, how can I add a value to a LineSegment3d? Should I use XData? But I only have one double to add? Is there any property that I can set a value?  I want to do this job in this way:
Code: [Select]
               List<LineSegment3d> lineLst = new List<LineSegment3d>(); //To hold the LineSegment3d

               LineSegment3d myLine = new LineSegment3d(pt1,pt2);
               myLine.SomeProperty = someValue;     //Assign the thickness value
               lineLst.Add(myLine);
Because I only have one extra value, I don't want to write a custom class to do this.
« Last Edit: June 30, 2012, 01:38:59 AM by 闻仲 »

zoltan

  • Guest
Re: How to assign a double value to a LineSegment3d?
« Reply #1 on: June 29, 2012, 01:57:50 PM »
Even though you think it will save you some typing by not having to create a custom class, it would benefit you greatly as your code grows to have a dedicated "Wall" class that encapsulates all of the methods and properties for what a wall is supposed to be and do.  It will make your code more readable, more maintainable, and easier to refactor when something changes.

Since you will not be able to descend from the LineSegment3d class directly (since it is sealed), you will need to composite it within your class.

Code - C#: [Select]
  1. public class Wall
  2. {
  3.   private LineSegment3d lineSegment;
  4.  
  5.   public double Thickness { get; set; }
  6.  
  7.   public Wall(Point3d startPoint, Point3d endPoint)
  8.   {
  9.     this.lineSegment = new LineSegment3d(startPoint, endPoint);
  10.   }
  11.  

Now you can access the line segment either directly, by exposing it to clients :

Code - C#: [Select]
  1. public LineSegment3d LineSegment
  2. {
  3.   get
  4.   {
  5.     return this.lineSegment;
  6.   }
  7. }
  8.  

...or expose only the methods and properties that you need and forward them to the contained LineSegment3d.

Code - C#: [Select]
  1. public double Length
  2. {
  3.   get
  4.   {
  5.     return this.lineSegment.Length;
  6.   }
  7. }
  8.  
  9. public Plane GetBisector()
  10. {
  11.   return this.lineSegment.GetBisector();
  12. }
  13.  


So now your implementation would look like this:
Code - C#: [Select]
  1. List<Wall> wallLst = new List<Wall>(); //To hold the walls
  2.  
  3. Wall myWall = new Wall(pt1,pt2);
  4. myWall.Thickness= someValue;     //Assign the thickness value
  5. wallLst.Add(myWall);
  6.  


Either way, since your LineSegment3d represents a wall, it would make more sense to have a "Wall" object that abstracts the concept of what a wall is and does into its own dedicated class that you can manage the interface for.
« Last Edit: June 29, 2012, 02:32:13 PM by zoltan »

TheMaster

  • Guest
Re: How to assign a double value to a LineSegment3d?
« Reply #2 on: June 29, 2012, 03:19:47 PM »
Even though you think it will save you some typing by not having to create a custom class, it would benefit you greatly as your code grows to have a dedicated "Wall" class that encapsulates all of the methods and properties for what a wall is supposed to be and do.  It will make your code more readable, more maintainable, and easier to refactor when something changes.

Since you will not be able to descend from the LineSegment3d class directly (since it is sealed), you will need to composite it within your class.


What if the wall is curved ?


zoltan

  • Guest
Re: How to assign a double value to a LineSegment3d?
« Reply #3 on: June 29, 2012, 05:13:49 PM »

What if the wall is curved ?

Than you simply change the LineSegment3d field to a Curve3d and add a new constructor.  One constructor will allow you to make straight walls and one for curved ones.  As long as all of the methods and properties of the Wall class either use the interface of the Curve3d or check to see if it is a LineSegment3d or CircularArc3d, everything else will work the same.

This is one of the advantages of composition verses inheritance.  The type of the composed object can change at run-time.

闻仲's original solution would make it much more difficult to add support for curved walls.
« Last Edit: June 29, 2012, 05:22:21 PM by zoltan »

waterharbin

  • Guest
Re: How to assign a double value to a LineSegment3d?
« Reply #4 on: June 29, 2012, 11:13:33 PM »
Hi,zoltan.
Thank you very much. Your solution is excellent. You taught me a lot.
I am not familiar with C# much. I used to type VC++ codes. I know in VC++, there will always be at least one deconstructor, but I don't see a deconstructor in your class. Is it that you don't need to write a deconstructor in C# yourself, the complier will do that for you?
And another question, do I need to take care the aftermath of the "Wall" class? Do I need to dispose it? The LineSegment3d don't need that.

TheMaster

  • Guest
Re: How to assign a double value to a LineSegment3d?
« Reply #5 on: June 30, 2012, 12:11:49 AM »
This is one of the advantages of composition verses inheritance.  The type of the composed object can change at run-time.

From what I understood, you were suggesting that the OP use composition because the LineSegment3d and
other related types are sealed.

Quote

Since you will not be able to descend from the LineSegment3d class directly (since it is sealed), you will need to
composite it within your class.


In any case, my point was that everyone seemed to be neglecting the fact that LineSegment3d is not an appropriate type to represent a wall, because of the potential for non-linear walls, and that it would be more correct to use a base type that is the ancestor of both linear and non-linear curves.  But, thanks for elaborating that point in detail.

The other thing being overlooked is that modeling walls in a building is at its essence, modeling a network topology. In other words, coordinates representing ends or intersections of walls (which are essentially nodes in a network) should not be literally stored within objects representing individual wall segments. 

Coordinates should be stored in data structures or objects representing network nodes, along with a unique ID (to support persistence), and be referenced from objects representing wall segments. Node objects would be stored in a separate list or dictionary (using the ID as the key) so they can be queried, modified, etc. Objects that represent wall segments would simply hold a reference to the Node instances that represent the endpoints of the wall. If geometry operations provided by Curve2d/3d and their derivatives need to be done, he can just create an instance of one when needed and discard it when finished using it.

So, if you are going to do it correctly (by modeling walls as nodes and edges in a graph or network), the composition thing isn't going to work well. That doesn't mean Curve2d/3d-based classes cannot be used for the operations they provide, it just means that they are not the right way to represent walls internally, because they by themselves can't represent edges or connections in a graph or network.

This pseudo-code was written entirely on the fly without no testing, and does none of the required error checking:

Code - C#: [Select]
  1. namespace conceptual
  2. {
  3.   // abstract wall base type for common operations
  4.  
  5.   public abstract class WallSegment  
  6.   {
  7.     protected WallSegment( WallNode startNode, WallNode endNode )
  8.     {
  9.       this.StartNode = startNode;
  10.       this.EndNode = endNode;
  11.     }
  12.    
  13.     public WallNode StartNode {get;set;}
  14.     public WallNode EndNode {get;set;}
  15.    
  16.     public Point2d StartPoint
  17.     {
  18.       get
  19.       {
  20.         return this.StartNode.Position;
  21.       }
  22.     }
  23.    
  24.     public Point2d EndPoint
  25.     {
  26.       get
  27.       {
  28.         return this.EndNode.Position;
  29.       }
  30.     }
  31.    
  32.     public double Length
  33.     {
  34.       get
  35.       {
  36.         Curve2d curve = this.Curve;  
  37.         return curve.GetLength(
  38.           curve.GetParameterOf( curve.StartPoint ),
  39.           curve.GetParameterOf( curve.EndPoint )
  40.         );
  41.       }
  42.     }
  43.     public abstract Curve2d Curve {get;}
  44.   }
  45.  
  46.   // Concrete linear wall segment:
  47.  
  48.   public class LinearWallSegment : WallSegment
  49.   {
  50.     public LinearWallSegment( WallNode startNode, WallNode endNode )
  51.       : base( startNode, endNode )
  52.     {
  53.     }
  54.     public override Curve2d Curve
  55.     {
  56.       get
  57.       {
  58.         return new LineSegment2d( this.StartNode.Position, this.EndNode.Position );
  59.       }
  60.     }
  61.   }
  62.  
  63.   // concrete arc wall segment:
  64.  
  65.   public class ArcWallSegment : WallSegment
  66.   {
  67.     public ArcWallSegment( WallNode startNode, WallNode endNode, double bulge, bool bulgeFlag )
  68.       : base( startNode, endNode )
  69.     {
  70.       this.Bulge = bulge;
  71.       this.BulgeFlag = bulgeFlag;
  72.     }
  73.    
  74.     public bool BulgeFlag {get;set;}
  75.     public double Bulge {get;set;}
  76.    
  77.     public override Curve2d Curve
  78.     {
  79.       get
  80.       {
  81.         return new CircularArc2d( this.StartPoint, this.EndPoint, this.Bulge, this.BulgeFlag );
  82.       }
  83.     }
  84.   }
  85.  
  86.  
  87.   // represents the position of the start or end of a wall:
  88.  
  89.   public class WallNode
  90.   {
  91.     public WallNode( int nodeId, Point2d position )
  92.     {
  93.       this.nodeId = nodeId;
  94.       this.Position = position;
  95.     }
  96.     int nodeId;
  97.    
  98.     public int Id {get { return this.nodeId;}}
  99.     public Point2d Position {get;set;}
  100.   }
  101.  
  102.   // Encapsulation of a model of connected walls:
  103.      
  104.   public class FloorPlanModel
  105.   {
  106.     int nextNode = 0;
  107.     List<WallSegment> walls = new List<WallSegment>();
  108.     List<WallNode> nodes = new List<WallNode>();
  109.  
  110.     public List<WallSegment> Walls {get {return walls;}}
  111.     public List<WallNode> Nodes {get {return nodes;}}
  112.  
  113.     // Add walls that span existing and/or new nodes:
  114.    
  115.     public LinearWallSegment AddLinearWallSegment( Point2d startPoint, Point2d endPoint )
  116.     {
  117.       LinearWallSegment wall = new LinearWallSegment(
  118.         GetNode( startPoint ),
  119.         GetNode( endPoint )
  120.       );
  121.       walls.Add( wall );
  122.       return wall;
  123.     }
  124.    
  125.     public ArcWallSegment AddArcWallSegment( Point2d startPoint, Point2d endPoint, double bulge, bool flag )
  126.     {
  127.       ArcWallSegment wall = new ArcWallSegment(
  128.         GetNode( startPoint ),
  129.         GetNode( endPoint ),
  130.         bulge,
  131.         flag
  132.       );
  133.       walls.Add( wall );
  134.       return wall;
  135.     }
  136.    
  137.     public WallNode GetNode( Point2d point, bool add = true )
  138.     {
  139.       WallNode node = nodes.FirstOrDefault( n => n.Position.GetDistanceTo( point ) < 0.1 );
  140.       if( node == null && add )
  141.       {
  142.         node = new WallNode( this.nextNode++, point );
  143.         nodes.Add( node );
  144.       }
  145.       return node;
  146.     }
  147.    
  148.     // Example Analytical operations that leverage the use of a network
  149.     // to model floor plans
  150.    
  151.     // This example returns all WallSegment objects
  152.     // connected to the node at the given point:
  153.    
  154.     public IEnumerable<WallSegment> GetWallSegmentsAt( Point2d point )
  155.     {
  156.       WallNode node = GetNode( point, false );
  157.       if( node != null )
  158.         return walls.Where( wall => wall.StartNode == node || wall.EndNode == node );
  159.       else
  160.         return Enumerable.Empty<WallSegment>();
  161.     }
  162.  
  163.     // Gets the WallSegments connected to the given wall segment:
  164.  
  165.     public IEnumerable<WallSegment> GetConnectedWalls( WallSegment ws )
  166.     {
  167.       WallNode a = ws.StartNode;
  168.       WallNode b = ws.EndNode;
  169.       return walls.Where( w => w != ws &&
  170.         w.StartNode == a || w.EndNode == a ||
  171.         w.StartNode == b || w.EndNode == b
  172.       );
  173.     }
  174.    
  175.     // Get the wall segment the given point is on,
  176.     // or is near to within the specified fuzz:
  177.    
  178.     public WallSegment GetWallSegmentAt( Point2d point, double fuzz )
  179.     {
  180.       Tolerance t = new Tolerance( Tolerance.Global.EqualVector, fuzz );
  181.       return walls.FirstOrDefault( w => w.Curve.IsOn( point, t ) );
  182.     }
  183.    
  184.     // Get the wall segment whose defining curve is closest to the given point:
  185.    
  186.     public WallSegment GetWallSegmentNearestTo( Point2d point )
  187.     {
  188.       return walls.Select( w => new {Wall = w, Distance = w.Curve.GetDistanceTo( point )} )
  189.                   .Aggregate( ( a, b ) => a.Distance < b.Distance ? a : b )
  190.                   .Wall;
  191.     }
  192.    
  193.     // Get the sum of the length of all wall segments:
  194.    
  195.     public double TotalLength
  196.     {
  197.       get
  198.       {
  199.         return walls.Sum( w => w.Length );
  200.       }
  201.     }
  202. }
  203.  
  204.  
« Last Edit: July 03, 2012, 01:49:32 PM by TheMaster »

waterharbin

  • Guest
Hi,TheMaster.
You are right, there are non-linear wall segments. If composition is not good, are you suggesting that I write a completely independent class with on father class.
At the present, the information I want to store are the length, thickness, the two endpoints. And I do not want to actually draw some "wall" on the screen. This class is just used to store the info. So, is this code OK?
Code: [Select]
public class Wall
    {
        public double Length { get; set; }
        public double Thickness { get; set; }

        public Point3d startPoint { get; set; }
        public Point3d endPoint { get; set; }
    }
If curved wall involved, I think the radian or curvature need to stored too.

I am not good at C#, dose there need a constructor or deconstuctor here?

Gasty

  • Newt
  • Posts: 90
Hi,

Nicely done Master, I'm working in a solution for concrete formwork, that use the same approach i.e. a graph to represent the wall connectivity, as for your example I'd would add a node degree to the WallNode class, as that permit some analysis of wall intersections and wall ends. Depending on the complexity of the solution you can resort to a full graph implementation like this: http://quickgraph.codeplex.com/ and so you don't have to implement some typical graph algorithms like A* search and others for graph traversal and analysis.

Gaston Nunez

TheMaster

  • Guest
Hi,

Nicely done Master, I'm working in a solution for concrete formwork, that use the same approach i.e. a graph to represent the wall connectivity, as for your example I'd would add a node degree to the WallNode class, as that permit some analysis of wall intersections and wall ends. Depending on the complexity of the solution you can resort to a full graph implementation like this: http://quickgraph.codeplex.com/ and so you don't have to implement some typical graph algorithms like A* search and others for graph traversal and analysis.

Gaston Nunez

Gaston - Sure, a comprehensive network or graph library would be right for real-world use, but the goal of the pseudo-code I posted was to illustrate the basic underlying concepts to an entry-level programmer, and for that purpose, a full-blown graph library would only obfuscate the very thing the simple example attempts to convey.

I try to make sample code for illustrating basic concepts as simple and concise as possible (e.g., a deliberate lack of required error checking), and it should not be viewed as an example of working or production code.
« Last Edit: June 30, 2012, 09:03:00 PM by TheMaster »

waterharbin

  • Guest
Hi,TheMaster. You are amazing. But I still sugest you to add "Thickness" to the WallSegment class. Maybe "Mass" too. When you want to calculate something, you will need those.

TheMaster

  • Guest
Hi,TheMaster. You are amazing. But I still sugest you to add "Thickness" to the WallSegment class. Maybe "Mass" too. When you want to calculate something, you will need those.

Thanks, but this isn't a project for me, just some sample code I threw into the discussion to help convey the concepts.  If you want to take an approach like this one, you can take the code and use it as a starting point, and add whatever members your problem domain requires.

Delegate

  • Guest
Hi,TheMaster.
You are right, there are non-linear wall segments. If composition is not good, are you suggesting that I write a completely independent class with on father class.
At the present, the information I want to store are the length, thickness, the two endpoints. And I do not want to actually draw some "wall" on the screen. This class is just used to store the info. So, is this code OK?
Code: [Select]
public class Wall
    {
        public double Length { get; set; }
        public double Thickness { get; set; }

        public Point3d startPoint { get; set; }
        public Point3d endPoint { get; set; }
    }
If curved wall involved, I think the radian or curvature need to stored too.

I am not good at C#, dose there need a constructor or deconstuctor here?

Here is something to read on deconstructor: http://msdn.microsoft.com/en-us/magazine/cc163392.aspx
Don't think you need to worry about it unless you are using unmanaged code within your class.
Probably better to use a constructor to help initialise your values. Otherwise the compiler will create a default one to initialize to its own default values.


waterharbin

  • Guest
Hi,TheMaster.Thanks for your permission. You give me a head start. I am going to change your code to fit my problems.

waterharbin

  • Guest
Hi,Delegate. Thanks. The native VC++ is unmanaged, while the C# is managed. In native VC++, I will always need to take care the constructor and the deconstructor, While in C#, I am free of that. Now I know that. Thanks for that.