SBoundary implements boundaries for use in scene graph
This defines the outer extent of the scene or of an element in the scene or of some branch in the scene.
The difference between this boundary and an n-dimensional surface, such as IFS, is that boundary must always have a well defined inside and an outside.
SBoundary does not necessarily follow the exact outer contours of the shape but just constructs a simple boundary shape where all points of the shape are guaranteed to be inside the boundary. Currently there are 3 boundary forms:
- "box"::Symbol which is a n-dimensional axis-aligned rectangle
- "ellipoid"::Symbol which is a n-dimensional axis-aligned ellipse
- "none"::Symbol is used, for example, when the boundary of a material node is requested.
In future we may add other boundary shapes, we may also allow a boundary to consist of a union of these other shapes.
In the case of box it is specified by two points, on opposite ends of diagonal, where edges are aligned with coordinate axes. |
In the case of ellipse: first parameter is centre and second determines the radius aligned with each coordinate axis (half the full height and width). |
The uses of SBoundary are:
- So we know how much to scale a given scene to fit in a given size.
- So that we know where to terminate arrows going in out out of of the shape.
- To detect if objects intersect (collision detect), or if one object is contained completely inside another.
First we create some types to work with
(1) -> DF ==> DoubleFloat Type: Void (2) -> PT ==> SCartesian(2) Type: Void (3) -> B ==> SBoundary PT Type: Void |
As discussed above SBoundary can have one of three forms: a null boundary, a box boundary and an ellipoid boundary. These 3 forms can be constructed as in 3,4 and 5 below:
(4) -> B1 := nullBoundary()$B (4) bound none Type: SBoundary(SCartesian(2)) (5) -> B2 := boxBoundary(spnt(20::DF,-20::DF),spnt(10::DF,10::DF))$B (5) bound box:pt(10.0,- 20.0)->pt(20.0,10.0) Type: SBoundary(SCartesian(2)) (6) -> B3 := ellipseBoundary(spnt(30::DF,10::DF),spnt(25::DF,5::DF))$B (6) bound ellipoid:pt(30.0,10.0)->pt(25.0,5.0) Type: SBoundary(SCartesian(2)) |
We can now do various operations on the boundaries that we have constructed.
Line (7) construct a union of two boundaries, that is, a boundary that completely encloses the input boundaries.
Line (8) is also a union, but this time, one of the operands is a point. We can think of this as extending the boundary to include the point.
Lines (9) and (10) demonstrate how we can test if a point is inside the boundary.
(7) -> B4 := sunion(B2,B3) (7) bound box:pt(5.0,- 20.0)->pt(55.0,15.0) Type: SBoundary(SCartesian(2)) (8) -> B5 := extendToPoint(B2,spnt(30::DF,10::DF))$B (8) bound box:pt(10.0,- 20.0)->pt(30.0,10.0) Type: SBoundary(SCartesian(2)) (9) -> containsPoint?(B2,spnt(15::DF,5::DF))$B (9) true Type: Boolean (10) -> containsPoint?(B2,spnt(0::DF,5::DF))$B (10) false Type: Boolean |
To show how boundary is used in a scenegraph, we first create some shapes:
In (12) we create the root node with a boundary so that we know what part we are interested in.
(11) -> view := boxBoundary(sipnt(-1,-1)$PT,sipnt(3,1)$PT) (11) bound box:pt(- 1.0,- 1.0)->pt(3.0,1.0) Type: SBoundary(SCartesian(2)) (12) -> sc := createSceneRoot(view)$Scene(PT) (12) scene root bound box:pt(- 1.0,- 1.0)->pt(3.0,1.0) #ch=0 Type: Scene(SCartesian(2)) (13) -> SHAPE ==> Record(shptype:Symbol,centre:PT,size:PT,fill:Boolean) Type: Void (14) -> sh1:SHAPE := ["ellipse"::Symbol,_ spnt(0::DF,0::DF)$PT,spnt(0.1::DF,0.2::DF)$PT,false] (14) [shptype= ellipse,_ centre= pt(0.0,0.0),size= pt(0.1,0.2),fill= false] Type: Record(shptype: Symbol,_ centre: SCartesian(2),size: SCartesian(2),fill: Boolean) (15) -> ellipse := addSceneShape(sc,sh1)$Scene(PT) (15) scene shape type=ellipse pt1=pt(0.0,0.0) pt2=pt(0.1,0.2) #ch=0 Type: Scene(SCartesian(2)) (16) -> text1 := addSceneText(sc,"long text",32::NNI,_ spnt(0.5::DF,0.4::DF)$PT)$Scene(PT) (16) scene text="long text" sz=32 p=pt(0.5,0.4) npt=[] #ch=0 Type: Scene(SCartesian(2)) (17) -> text2 := addSceneText(sc,"A",32::NNI,_ spnt(0.1::DF,0.3::DF)$PT)$Scene(PT) (17) scene text="A" sz=32 p=pt(0.1,0.3) npt=[] #ch=0 Type: Scene(SCartesian(2)) (18) -> line := addSceneLine(sc,_ [spnt(0.3::DF,0.7::DF)$PT,spnt(0.1::DF,0.9::DF)$PT]) (18) scene line [[pt(0.3,0.7),pt(0.1,0.8999999999999999)]] #ch=0 Type: Scene(SCartesian(2)) (19) -> arrow := addSceneArrows(sc,_ [[spnt(0.5::DF,-0.2::DF)$PT,spnt(1.5::DF,0.5::DF)$PT]],_ "fixed"::Symbol,2::DF) (19) scene arrows pts=[[pt(0.5,- 0.2),_ pt(1.5,0.5)]] m=fixed sz=2.0 #ch=0 Type: Scene(SCartesian(2)) |
We have now created various nodes:
- An ellipse.
- Two text nodes (one long and one short).
- A line.
- An arrow.
We can now go on to get the boundaries for these nodes and draw them under the material node (so that they are drawn in blue).
(20) -> mt2 := addSceneMaterial(sc,3::DF,"blue","green")$Scene(PT) (20) scene material lw=3.0 lc="blue" fc="green" mo=1.0 #ch=0 Type: Scene(SCartesian(2)) (21) -> scenebd:SBoundary(PT) := boundary(sc) (21) bound box:pt(- 0.1,- 0.2)->pt(1.5,0.8999999999999999) Type: SBoundary(SCartesian(2)) (22) -> addSceneShape(mt2,scenebd) (22) scene shape type=rect pt1=pt(- 0.1,- 0.2)_ pt2=vec(1.6,1.0999999999999999) #ch=0 Type: Scene(SCartesian(2)) (23) -> ellipsebd:SBoundary(PT) := boundary(ellipse) (23) bound ellipoid:pt(0.0,0.0)->pt(0.1,0.2) Type: SBoundary(SCartesian(2)) (24) -> addSceneShape(mt2,ellipsebd) (24) scene shape type=rect pt1=pt(0.0,0.0) pt2=vec(0.1,0.2) #ch=0 Type: Scene(SCartesian(2)) (25) -> text1bd:SBoundary(PT) := boundary(text1) (25) bound box:pt(0.5,0.4)->pt(1.076,0.528) Type: SBoundary(SCartesian(2)) (26) -> addSceneShape(mt2,text1bd) (26) scene shape type=rect pt1=pt(0.5,0.4)_ pt2=vec(0.5760000000000001,0.128) #ch=0 Type: Scene(SCartesian(2)) (27) -> text2bd:SBoundary(PT) := boundary(text2) (27) bound box:pt(0.1,0.3)->pt(0.164,0.428) Type: SBoundary(SCartesian(2)) (28) -> addSceneShape(mt2,text2bd) (28) scene shape type=_ rect pt1=pt(0.1,0.3) pt2=vec(0.064,0.128) #ch=0 Type: Scene(SCartesian(2)) (29) -> linebd:SBoundary(PT) := boundary(line) (29) bound box:pt(0.1,0.7)->pt(0.3,0.8999999999999999) Type: SBoundary(SCartesian(2)) (30) -> addSceneShape(mt2,linebd) (30) scene shape type=rect pt1=pt(0.1,0.7) pt2= vec(0.19999999999999998,0.19999999999999996) #ch=0 Type: Scene(SCartesian(2)) (31) -> arrowbd:SBoundary(PT) := boundary(arrow) (31) bound box:pt(0.5,- 0.2)->pt(1.5,0.5) Type: SBoundary(SCartesian(2)) (32) -> addSceneShape(mt2,arrowbd) (32) scene shape type=rect pt1=pt(0.5,- 0.2) pt2=vec(1.0,0.7) #ch=0 Type: Scene(SCartesian(2)) (33) -> writeSvg(sc,"testGraph/boundary1.svg") Type: Void |
So here are the shapes that we constructed with their boundaries drawn in blue. |
We will now construct some more shapes and this time we will draw arrows between them:
(34) -> sc2 := createSceneRoot(view)$Scene(PT) (34) scene root bound box:pt(- 1.0,- 1.0)->pt(3.0,1.0) #ch=0 Type: Scene(SCartesian(2)) (35) -> sh2:SHAPE := ["ellipse"::Symbol,_ spnt(0::DF,0::DF)$PT,spnt(0.1::DF,0.2::DF)$PT,false] (35) [shptype= ellipse,_ centre= pt(0.0,0.0),size= pt(0.1,0.2),fill= false] Type: Record(shptype: Symbol,_ centre: SCartesian(2),size: SCartesian(2),fill: Boolean) (36) -> ellipse2 := addSceneShape(sc2,sh2)$Scene(PT) (36) scene shape type=ellipse pt1=pt(0.0,0.0) pt2=pt(0.1,0.2) #ch=0 Type: Scene(SCartesian(2)) (37) -> sh3:SHAPE := ["rect"::Symbol,_ spnt(0.4::DF,-0.2::DF)$PT,spnt(0.2::DF,0.4::DF)$PT,false] (37) [shptype= rect,_ centre= pt(0.4,- 0.2),size= pt(0.2,0.4),fill= false] Type: Record(shptype: Symbol,_ centre: SCartesian(2),size: SCartesian(2),fill: Boolean) (38) -> ellipse3 := addSceneShape(sc2,sh3)$Scene(PT) (38) scene shape type=rect pt1=pt(0.4,- 0.2) pt2=pt(0.2,0.4) #ch=0 Type: Scene(SCartesian(2)) (39) -> sh4:SHAPE := ["rect"::Symbol,_ spnt(-0.1::DF,0.3::DF)$PT,spnt(0.2::DF,0.4::DF)$PT,false] (39) [shptype= rect,_ centre= pt(- 0.1,0.3),size= pt(0.2,0.4),fill= false] Type: Record(shptype: Symbol,_ centre: SCartesian(2),size: SCartesian(2),fill: Boolean) (40) -> ellipse4 := addSceneShape(sc2,sh4)$Scene(PT) (40) scene shape type=rect pt1=pt(- 0.1,0.3) pt2=pt(0.2,0.4) #ch=0 Type: Scene(SCartesian(2)) (41) -> sh5:SHAPE := ["ellipse"::Symbol,_ spnt(0.5::DF,0.5::DF)$PT,spnt(0.1::DF,0.2::DF)$PT,false] (41) [shptype= ellipse,_ centre= pt(0.5,0.5),size= pt(0.1,0.2),fill= false] Type: Record(shptype: Symbol,_ centre: SCartesian(2),size: SCartesian(2),fill: Boolean) (42) -> ellipse5 := addSceneShape(sc2,sh5)$Scene(PT) (42) scene shape type=ellipse pt1=pt(0.5,0.5) pt2=pt(0.1,0.2) #ch=0 Type: Scene(SCartesian(2)) |
Having constructed the shapes we will now get their boundaries:
(43) -> ellipsebd2:SBoundary(PT) := boundary(ellipse2) (43) bound ellipoid:pt(0.0,0.0)->pt(0.1,0.2) Type: SBoundary(SCartesian(2)) (44) -> ellipsebd3:SBoundary(PT) := boundary(ellipse3) (44) bound box:pt(0.4,- 0.2)->pt(0.6000000000000001,0.2) Type: SBoundary(SCartesian(2)) (45) -> ellipsebd4:SBoundary(PT) := boundary(ellipse4) (45) bound box:pt(- 0.1,0.3)->pt(0.1,0.7) Type: SBoundary(SCartesian(2)) (46) -> ellipsebd5:SBoundary(PT) := boundary(ellipse5) (46) bound ellipoid:pt(0.5,0.5)->pt(0.1,0.2) Type: SBoundary(SCartesian(2)) (47) -> line2:List PT := link(ellipsebd2,ellipsebd3) (47) [pt(0.10000000000000002,0.0),pt(0.39999999999999997,0.0)] Type: List(SCartesian(2)) (48) -> line3:List PT := link(ellipsebd2,ellipsebd4) (48) [pt(0.0,0.20000000000000004),pt(0.0,0.30000000000000004)] Type: List(SCartesian(2)) (49) -> line4:List PT := link(ellipsebd3,ellipsebd5) (49) [pt(0.5,0.2),pt(0.5,0.29999999999999993)] Type: List(SCartesian(2)) (50) -> line5:List PT := link(ellipsebd4,ellipsebd5) (50) [pt(0.1,0.5),pt(0.39999999999999997,0.5)] Type: List(SCartesian(2)) (51) -> line6:List PT := link(ellipsebd2,ellipsebd5) (51) [pt(0.0894427190999916,0.0894427190999916), pt(0.4105572809000084,0.4105572809000084)] Type: List(SCartesian(2)) (52) -> line7:List PT := link(ellipsebd3,ellipsebd4) (52) [pt(0.39999999999999997,0.10000000000000003),pt(0.1,0.4)] Type: List(SCartesian(2)) (53) -> mt3 := addSceneMaterial(sc2,3::DF,"red","green")$Scene(PT) (53) scene material lw=3.0 lc="red" fc="green" mo=1.0 #ch=0 Type: Scene(SCartesian(2)) |
We can now draw arrows between the shapes:
(54) -> arrow2 := addSceneArrows(mt3,[line2],"fixed"::Symbol,2::DF) (54) scene arrows pts=[[_ pt(0.10000000000000002,0.0),pt(0.39999999999999997,0.0)]] m=fixed sz=2.0 #ch=0 Type: Scene(SCartesian(2)) (50) -> arrow3 := addSceneArrows(mt3,[line3],"fixed"::Symbol,2::DF) (55) scene arrows pts=[[_ pt(0.0,0.20000000000000004),pt(0.0,0.30000000000000004)]] m=fixed sz=2.0 #ch=0 Type: Scene(SCartesian(2)) (50) -> arrow4 := addSceneArrows(mt3,[line4],"fixed"::Symbol,2::DF) (56) scene arrows pts=[[_ pt(0.5,0.2),pt(0.5,0.29999999999999993)]] m=fixed sz=2.0 #ch=0 Type: Scene(SCartesian(2)) (50) -> arrow5 := addSceneArrows(mt3,[line5],"fixed"::Symbol,2::DF) (57) scene arrows pts=[[_ pt(0.1,0.5),pt(0.39999999999999997,0.5)]] m=fixed sz=2.0 #ch=0 Type: Scene(SCartesian(2)) (50) -> arrow6 := addSceneArrows(mt3,[line6],"fixed"::Symbol,2::DF) (58) scene arrows pts= [ [pt(0.0894427190999916,0.0894427190999916), pt(0.4105572809000084,0.4105572809000084)] ] m=fixed sz=2.0 #ch=0 Type: Scene(SCartesian(2)) (50) -> arrow7 := addSceneArrows(mt3,[line7],"fixed"::Symbol,2::DF) (59) scene arrows pts=[[_ pt(0.39999999999999997,0.10000000000000003),pt(0.1,0.4)]] m=fixed sz=2.0 #ch=0 Type: Scene(SCartesian(2)) (60) -> writeSvg(sc2,"testGraph/boundary2.svg") Type: Void |
So we can see that this form of addSceneArrows draws the arrow upto the boundary of the nodes that it is drawing the arrows between. |