I give the following input to Dot:
digraph G {
subgraph cluster1 {
fontsize = 20;
label = "Group 1";
A -> B -> C -> D;
style = "dashed";
}
subgraph {
O [shape=box];
}
subgraph cluster2 {
fontsize = 20;
label = "Group 2";
Z -> Y -> X -> W [dir=back];
style = "dashed";
}
D -> O [constraint=false];
W -> O [constraint=false, dir=back];
}
And it produces:
How can I align node O so that it has the same rank as D and W? That is, a graph that looks like:
A Z
| |
B Y
| |
C X
| |
D-O-W
Adding
{ rank=same; D; O; W; }
yields the error
Warning: D was already in a rankset, ignored in cluster G
Warning: W was already in a rankset, ignored in cluster G
I'm thinking I can hack it by adding invisible nodes and edges to the subgraph of O, but I was wondering if I was missing some Dot magic.
You could use an approach with rankdir=LR and use constraint=false for the edges inside the clusters:
digraph G {
rankdir=LR;
subgraph cluster1 {
fontsize = 20;
label = "Group 1";
rank=same;
A -> B -> C -> D [constraint=false];
style = "dashed";
}
subgraph cluster2 {
fontsize = 20;
label = "Group 2";
rank=same;
Z -> Y -> X -> W [dir=back, constraint=false];
style = "dashed";
}
O [shape=box];
D -> O -> W;
}
It's not dot magic :-), but it achieves this:
Hacking with invisible nodes does also work:
digraph G {
subgraph cluster1 {
fontsize = 20;
label = "Group 1";
A -> B -> C -> D;
style = "dashed";
}
subgraph {
O1[style=invis];
O2[style=invis];
O3[style=invis];
O [shape=box];
O1 -> O2 -> O3 -> O [style=invis];
}
subgraph cluster2 {
fontsize = 20;
label = "Group 2";
Z -> Y -> X -> W [dir=back];
style = "dashed";
}
edge[constraint=false];
D -> O -> W;
}
The result is almost identical:
Related
Suppose I have types like this:
type C =
| W of int
| Z of int
type B =
{
C : C
D : int
}
type A =
| X of int
| Y of B
And I would like to do something for the Z case only:
let a =
Y
{
C = Z 123
D = 456
}
match a with
| X _ -> ()
| Y b ->
match b.C with
| W _ -> ()
| Z z -> printfn "%i" z
Is there a way to achieve this in a single match?
Can this be made more concise?
Yes, there is indeed! Patterns can be nested, that's their primary point. You can match on Y, and inside that match on the fields of B, and inside that match on C.
Like this:
match a with
| X _ -> ()
| Y { C = Z z } -> printfn "%i" z
| Y { C = W _ } -> ()
And since you're returning unit in both non-Y.C.Z cases, you can combine them in a catch-all pattern:
match a with
| Y { C = Z z } -> printfn "%i" z
| _ -> ()
I tried to create a function that takes two integers a,b as input and return 5 if a=1 b=2 and 6 otherwise, Here is what I did:
let examplef (a:int), (b:int)=
match a,b with
|1,2 -> 5
|_,_->6;;
It gives this error: "The pattern discriminator 'examplef' is not defined."
I ask this question because of the error in this code:
type Team = string
type Goals = Goals of int
type Points = Points of int
type Fixture = Team * Team
type Result = (Team * Goals) * (Team * Goals)
type Table = Map<Team,Points>
let league =["Chelsea"; "Spurs"; "Liverpool"; "ManCity"; "ManUnited"; "Arsenal"; "Everton"; "Leicester"]
let pointsMade (a: Result)=
match a with
|((b,Goals bg),(c,Goals cg))-> if b<c then ((b,Points 0),(c, Points 3))
elif b=c then ((b,Points 1),(c,Points 1))
else ((b, Points 3),(c, Points 0))
I get an error when trying to define the following function:
let updateTable (t:Table, r: Result)=
let pointmade = pointsMade r
match pointmade with
|((f,Points s),(f1,Points s1))-> match Map.tryFind f t Map.tryFind f1 t with
|None, None -> t
|Some Points x, Some Points y ->t .Add (f, Points s+x1) .Add(f1, Points s1+y1)
When I hover the mouse over the first "Map.tryFind f t" It says "This value is not a function and cannot be applied. Also, there is an error with t .Add (f, Points s+x1) .Add(f1, Points s1+y1) it says: "Successive arguments should be separated by space and tuples and arguments involving functions or method applications should be parenthesized".
Please help
It looks like you're confusing tuple and curried arguments.
Examples with a single tuple argument (parenthesis are requiered).
signature: int * int -> int
//let example1 (a: int, b:int) =
let example1 (a, b) =
match a, b with
| 1, 2 -> 5
| _ -> 6
//let example2 (t: int * int) =
let example2 t =
match t with
| 1, 2 -> 5
| _ -> 6
Example with two curried arguments:
signature: int-> int -> int
//let example3 (a: int) (b: int) =
let example3 a b =
match a, b with
| 1, 2 -> 5
| _ -> 6
Anyway, The code that work looks like this:
open System.Security.Cryptography
open System.Threading
type Team = string
type Goals = Goals of int
type Points = Points of int
type Fixture = Team * Team
type Result = (Team * Goals) * (Team * Goals)
type Table = Map<Team,Points>
let league =["Chelsea"; "Spurs"; "Liverpool"; "ManCity"; "ManUnited"; "Arsenal"; "Everton"; "Leicester"]
let pointsMade (a: Result)=
match a with
|((b,Goals bg),(c,Goals cg))-> if bg<cg then ((b,Points 0),(c, Points 3))
elif bg=cg then ((b,Points 1),(c,Points 1))
else ((b, Points 3),(c, Points 0))
let initEntry (name:Team)=(name, Points 0)
let initializeTable l = Map.ofList (List.map initEntry l)
let updateTable (t:Table, r: Result)=
let pointmade = pointsMade r
match pointmade with
|((f,Points s),(f1,Points s1))-> match Map.tryFind f t, Map.tryFind f1 t with
|None, None -> t
|Some x, Some y-> match x,y with
| Points x1 , Points y1 -> t |> Map.add f (Points(x1+s)) |> Map.add f1 (Points (y1+s1))
|None, Some y -> match y with
| Points y1 -> t.Add(f,Points s) .Add(f1, Points (s1+y1))
|Some x, None -> match x with
| Points x1 -> t.Add(f,Points (s+x1)) .Add(f1, Points s1)
let rec weekendUpdate (t:Table , rl:Result list)=
match rl with
|[]->t
|ai::at-> weekendUpdate(updateTable(t,ai),at)
let rec seasonUpdate (t:Table, sll: Result list list)=
match sll with
|[]->t
|ah::at-> seasonUpdate(weekendUpdate(t,ah),at)
let less((s1,n1):Team * Points, (s2,n2):Team * Points) =
match n1,n2 with
|Points m1,Points m2 ->if m1<m2 then true
else false
let rec myinsert item lst =
match lst with
| [] -> [item]
| x::xs -> if less(item,x) then x::(myinsert item xs) else item::lst
let rec isort lst =
match lst with
| [] -> []
| x::xs -> myinsert x (isort xs)
let showStandings (t:Table) = isort (Map.toList t)
In your "working code", in the pointsMade function you do not need to use pattern matching, you can simply use a let binding.
let pointsMade (r: Result) =
let (t1, Goals g1), (t2, Goals g2) = r
if g1 < g2 then (t1, Points 0), (t2, Points 3)
elif g1 = g2 then (t1, Points 1), (t2, Points 1)
else (t1, Points 3), (t2, Points 0)
The updateTable function also can be re-written in more concise way by using some addPoints function to avoid repeating the same thing for each team.
let addPoints (team: Team, Points points) (table: Table) =
match Map.tryFind team table with
| None -> table
| Some (Points p) -> Map.add team (Points (points + p)) table
let updateTable (table: Table, result: Result) =
let pts1, pts2 = pointsMade result
table |> addPoints pts1 |> addPoints pts2
I have worked on this quite a while and is stuck with this bug.
We have build a ray-tracer in F# for a school project. (Link explaining Ray tracer: https://blog.frogslayer.com/kd-trees-for-faster-ray-tracing-with-triangles/)
We have a made hit function for triangles, boundingboxes, a KD tree to handle figures with thousands of triangles, such as the Stanford Bunny and a traverse algorithm for the KD tree.
We have debugged both the creation of the KD tree, made sure to add a epsilon value for float points and checked that duplicates between the boundingboxes are not removed. We are certain that we split the list of shapes in the scene corretly, but we get "holes" in the figure we try to render.
This is our KD tree implementation and I've attached pictures of the holes:
Stanford Bunny
Helix
module TmKdtree
open Point
open Shapes
type BoundingBox = BasicShape.BoundingBox
type Shape = BasicShape.Shape
type TmKdtree =
| Leaf of BasicShape.Triangle list * BoundingBox
| Node of BasicShape.Triangle list * TmKdtree * TmKdtree * BoundingBox * (string*Point)
let epsilon = 0.001
//Making a boundingbox for the KD-tree, by finding max H point in the boundingboxlist and min l point in the boundingbox list.
let mkKdBbox (shapes : BasicShape.Triangle list) : BoundingBox =
let shapeX = List.map(fun x -> x:> Shape) shapes
let sbbox = List.map (fun (c:Shape) -> c.getBounding().Value) shapeX
let bL = List.map (fun (b:BasicShape.BoundingBox) -> b.getL) sbbox
let bH = List.map (fun (b:BasicShape.BoundingBox) -> b.getH) sbbox
let minX = List.minBy (fun x -> Point.getX x) bL
let minY = List.minBy (fun x -> Point.getY x) bL
let minZ = List.minBy (fun x -> Point.getZ x) bL
let maxX = List.maxBy (fun x -> Point.getX x) bH
let maxY = List.maxBy (fun x -> Point.getY x) bH
let maxZ = List.maxBy (fun x -> Point.getZ x) bH
{p1=(mkPoint (Point.getX minX - epsilon) (Point.getY minY - epsilon) (Point.getZ minZ - epsilon) )
; p2=(mkPoint (Point.getX maxX + epsilon) (Point.getY maxY + epsilon) (Point.getZ maxZ + epsilon) )}
//Splitting existing boundingbox according to left and right list of shapes
let BoundingBoxL (bbox:BoundingBox) axis midp : BoundingBox =
match axis with
| "x" -> {p1 = bbox.getL - epsilon; p2 = Point.mkPoint ((Point.getX midp)) ((Point.getY (bbox.getH))) ((Point.getZ (bbox.getH))) + epsilon}
| "y" -> {p1 = bbox.getL - epsilon; p2 = Point.mkPoint (Point.getX (bbox.getH)) ((Point.getY midp)+epsilon) ((Point.getZ (bbox.getH))) + epsilon }
| "z" -> {p1 = bbox.getL - epsilon; p2 = Point.mkPoint (Point.getX (bbox.getH)) (Point.getY (bbox.getH)) (Point.getZ midp) + epsilon}
let BoundingBoxR (bbox:BoundingBox) axis midp : BoundingBox =
match axis with
| "x" -> {p1 = (Point.mkPoint (Point.getX midp) (Point.getY (bbox.getL)) (Point.getZ (bbox.getL))) - epsilon; p2 = bbox.getH + epsilon}
| "y" -> {p1 = (Point.mkPoint (Point.getX (bbox.getL)) (Point.getY midp) (Point.getZ (bbox.getL))) - epsilon; p2 = bbox.getH + epsilon}
| "z" -> {p1 = (Point.mkPoint (Point.getX (bbox.getL)) (Point.getY (bbox.getL)) (Point.getZ midp)) - epsilon; p2 = bbox.getH + epsilon}
//Get left node
let getLeft s =
match s with
| Node(_,l,_,_,_) -> l
| Leaf(_,_) as leaf -> leaf
let getRight s =
match s with
| Node(_,_,r,_,_) -> r
| Leaf(_,_) as leaf -> leaf
//Get the triangle list
let getShapes s =
match s with
| Node(b,_,_,_,_) -> b
| Leaf(b,_) -> b
let getAxis s =
match s with
| Node(_,_,_,_,a) -> a
| Leaf(_,_) -> failwith "leaf ramt af axis"
//Get bounding box
let getBox s =
match s with
| Node(_,_,_,b,_) -> Some b
| Leaf(_,b) -> Some b
let closestHit (triList : BasicShape.Triangle list) ray =
let sndRects = List.map(fun x -> x:> Shape) triList
let min = List.map(fun (x:Shape) -> x.hit ray) sndRects |> List.choose id
match min with
|[] -> None
|_ -> Some(List.minBy (fun (di, nV, mat) -> di) min)
let searchLeaf leaf ray t' =
match leaf with
| Leaf(s,_) -> let hit = closestHit s ray
match hit with
|Some(f,_,_) -> if (f<t') then Some hit else None
|None -> None
| Node(_,_,_,_,_) -> failwith "Expected leaf"
let order(d, left, right) =
if d > 0.0
then (left, right)
else (right, left)
let rec search node ray t t' =
match node with
|Leaf(_,_) -> searchLeaf node ray t'
|Node(_,_,_,_,a') ->
let direction = Ray.getDirection ray (fst a')
let origin = Ray.getOrigin ray (fst a')
let nodeValue = Point.getFromAxis (snd a') (fst a')
if(direction) = 0.0 then
printfn("%s") "flatsite"
if(origin <= nodeValue) then search (getLeft node) ray t t'
else search (getRight node) ray t t'
else
let tHit = (nodeValue - origin) / direction
let (fst, snd) = order(direction,getLeft node, getRight node)
if tHit <= t || tHit < 0.0 then
search snd ray t t'
else if tHit >= t' then
search fst ray t t'
else
match search fst ray t tHit with
|Some hit -> Some hit
|_ -> search snd ray tHit t'
let traverse tree ray (bawx:BasicShape.BoundingBox) =
match(bawx).hit(ray) with
|Some(t,t') -> search tree ray t t'
|None -> None
//Finding the midpoint in the triangles in Shapes-list - we do this (recursively) to find out what axis to split
let rec mkTmKdtree (shapes : BasicShape.Triangle list) (box:BasicShape.BoundingBox) =
//Finding biggest dimension in the shapes list
let axis = snd (box.getLongestAxis)
let axisMidPoint =
let midPoint = List.fold (fun acc (ele:BasicShape.Triangle) -> (acc + ele.getMidPoint())) (Point.mkPoint 0.0 0.0 0.0) shapes
let avgMid = midPoint / float(shapes.Length)
avgMid
let rec largerThanSplit (xs:BasicShape.Triangle list) =
let results = List.choose(fun (elem:BasicShape.Triangle) ->
match axis with
|"x" -> if (Point.getX (elem.getMidPoint())) >= (Point.getX axisMidPoint) then Some elem else None
|"y" -> if (Point.getY (elem.getMidPoint())) >= (Point.getY axisMidPoint) then Some elem else None
|"z" -> if (Point.getZ (elem.getMidPoint())) >= (Point.getZ axisMidPoint) then Some elem else None) xs
results
let rec lessThanSplit (xs:BasicShape.Triangle list) =
let results = List.choose(fun (elem:BasicShape.Triangle) ->
match axis with
|"x" -> if ((Point.getX (elem.getMidPoint())) <= (Point.getX (axisMidPoint))) then Some elem else None
|"y" -> if ((Point.getY (elem.getMidPoint())) <= (Point.getY (axisMidPoint))) then Some elem else None
|"z" -> if ((Point.getZ (elem.getMidPoint())) <= (Point.getZ (axisMidPoint))) then Some elem else None) xs
results
//Creating the left and right list from the above
let rightTest = largerThanSplit shapes
let leftTest = lessThanSplit shapes
//If one of the trees are empty, we add make left and right equivelant.
let left = if(leftTest.IsEmpty && rightTest.Length > 0) then rightTest else leftTest
let right = if(rightTest.IsEmpty && leftTest.Length > 0) then leftTest else rightTest
//Check for duplicates among the lists.
if(((float(left.Length+right.Length-shapes.Length)/float(shapes.Length)) < 0.4) && left.Length <> shapes.Length && right.Length<>shapes.Length) then
let leftTree = mkTmKdtree left (BoundingBoxL box axis axisMidPoint)
let rightTree = mkTmKdtree right (BoundingBoxR box axis axisMidPoint)
Node(shapes,leftTree, rightTree, (box),(axis,axisMidPoint))
else Leaf(shapes, (box))
Thank you for the responses! I turned out that the bug was in our reflections of the figures, and had nothing to do with the data structure of the program.
But thank you anyway! :-)
I am trying to align three or more subgraphs using dot files and graphviz. I think my problem is best shown using a few examples:
My first try:
digraph FutopJobFlow {
rankdir=LR;
node [shape=box]
compound=true
subgraph clusterA {label = " A ";
A -> a1;
a1 -> a2;
a2 -> a3;
}
subgraph clusterB {label = " B ";
B -> b1;
b1 -> b2;
}
subgraph clusterC {label = " C ";
C -> c1;
c1 -> c2;
}
A -> B [lhead=clusterB];
B -> C [lhead=clusterC];
X -> A [lhead=clusterA];
Y -> B [lhead=clusterB];
Z -> C [lhead=clusterC];
}
giving this result:
Here the individual subgraphs looks like i would like but the are not aligned. I therefore tried wit the command rank:
digraph FutopJobFlow {
rankdir=LR;
node [shape=box]
compound=true
subgraph clusterA {label = " A ";
A -> a1;
a1 -> a2;
a2 -> a3;
}
subgraph clusterB {label = " B ";
B -> b1;
b1 -> b2;
}
subgraph clusterC {label = " C ";
C -> c1;
c1 -> c2;
}
{rank=same; A; B; C;}
A -> B [lhead=clusterB];
B -> C [lhead=clusterC];
X -> A [lhead=clusterA];
Y -> B [lhead=clusterB];
Z -> C [lhead=clusterC];
}
which result in this graph:
Here the alignment looks good but now the 'A', 'B' and 'C' is no longer inside the subgraphs!
I have tried several other ways to achieve both the alignment and that 'A', 'B' and 'C' is inside their respective subgraphs but with no succes.
Can anybody help?
# Marapet - Thanks it is almost perfect now - it looks like this when i add the 'constraint=false' parameter:
Graph with constraint parameter
It would be perfect if the subgraph 'A' is above 'B' which again is above 'C'.
In the first version of your graph, you can disable the effect on node ranking for the edges between A-B and B-C by adding the attribute constraint=false:
A -> B [lhead=clusterB, constraint=false];
B -> C [lhead=clusterC, constraint=false];
The subgraphs should then be aligned.
I was trying to build a binary tree in F# but when I tried to test my code, I met the problem above.
Here is my code:
type TreeNode<'a> = { Key: int; Val: 'a }
type Tree<'a> = { LT: Tree<'a> option; TreeNode: TreeNode<'a>; RT: Tree<'a> option; }
//insert a node according to Binary Tree operation
let rec insert (node: TreeNode<'a>) (tree: Tree<'a> option) =
match tree with
| None -> {LT = None; RT = None; TreeNode = node }
| Some t when node.Key < t.TreeNode.Key -> insert node t.LT
| Some t when node.Key > t.TreeNode.Key -> insert node t.RT
let t = seq { for i in 1 .. 10 -> { Key = i; Val = i } }|> Seq.fold (fun a i -> insert i a) None
Your insert function takes option<Tree<'T>> but returns Tree<'T>. When performing the fold, you need to keep state of the same type - so if you want to use None to represent empty tree, the state needs to be optional type.
The way to fix this is to wrap the result of insert in Some:
let tree =
seq { for i in 1 .. 10 -> { Key = i; Val = i } }
|> Seq.fold (fun a i -> Some(insert i a)) None
I worked it out now... It should be like below:
type TreeNode<'a> = { Key: int; Val: 'a }
type Tree<'a> = { TreeNode: TreeNode<'a>; RT: Tree<'a> option; LT: Tree<'a> option; }
//insert a node according to Binary Tree operation
let rec insert (node: TreeNode<'a>) (tree: Tree<'a> option) =
match tree with
| None -> {LT = None; RT = None; TreeNode = node }
| Some t when node.Key < t.TreeNode.Key -> {TreeNode = t.TreeNode; LT = Some(insert node t.LT); RT = t.RT}
| Some t when node.Key > t.TreeNode.Key -> {TreeNode = t.TreeNode; RT = Some(insert node t.RT); LT = t.LT}
let t = seq { for i in 1 .. 10-> { Key = i; Val = i } }|> Seq.fold (fun a i -> Some(insert i a)) None