----------------------------------------------- --------------------------------- -- -- -- OCARINA COMPONENTS -- -- -- -- O C A R I N A . D I A . P R I N T E R . O P T I M I Z E -- -- -- -- B o d y -- -- -- -- Copyright (C) 2005-2007, GET-Telecom Paris. -- -- -- -- Ocarina is free software; you can redistribute it and/or modify -- -- it under terms of the GNU General Public License as published by the -- -- Free Software Foundation; either version 2, or (at your option) any -- -- later version. Ocarina is distributed in the hope that it will be -- -- useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -- -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -- -- Public License for more details. You should have received a copy of the -- -- GNU General Public License distributed with Ocarina; see file COPYING. -- -- If not, write to the Free Software Foundation, 51 Franklin Street, Fifth -- -- Floor, Boston, MA 02111-1301, USA. -- -- -- -- As a special exception, if other files instantiate generics from this -- -- unit, or you link this unit with other files to produce an executable, -- -- this unit does not by itself cause the resulting executable to be -- -- covered by the GNU General Public License. This exception does not -- -- however invalidate any other reasons why the executable file might be -- -- covered by the GNU Public License. -- -- -- -- Ocarina is maintained by the Ocarina team -- -- (ocarina-users@listes.enst.fr) -- -- -- ------------------------------------------------------------------------------ -- This package contains the top-level procedures to optimize the -- position of the objects in the Dia file with Ada.Text_IO; use Ada.Text_IO; with DOM.Core; with DOM.Core.Nodes; with DOM.Core.Documents; -- Using the parser stuff to avoid duplicating the source with Ocarina.Dia.Parser.Core; with Ocarina.Dia.Parser.Core.Attributes; with Ocarina.Dia.Parser.Core.Diagram; with Ocarina.Dia.Parser.Core.Nodes; with Ada.Numerics.Elementary_Functions; use Ada.Numerics.Elementary_Functions; package body Ocarina.Dia.Printer.Optimize is -- Translates the XML Document to a compound graph -- for optimization function XML_To_Graph (Doc : DOM.Core.Document) return Compound_Graph is use Ocarina.Dia; Document : constant DOM.Core.Node := DOM.Core.Documents.Get_Element (Doc); NL : constant Ocarina.Dia.Parser.Core.Node_List := Parser.Core.Diagram.Process_Diagram (Document); N : Parser.Core.Node; S : Unbounded_String; Nat : Natural; Num_Objects : constant Integer := Parser.Core.Count_Objects (NL); Num_Links : constant Integer := Parser.Core.Nodes.Length (NL) - Num_Objects; Null_Point : constant Printer.Misc.Point := (0.0, 0.0); Null_Rectangle : constant Printer.Misc.Rectangle := (Null_Point, Null_Point); Num_Ports : Natural; Num_Connections : Natural; Ports : Point_Array_Ptr; G : Compound_Graph; Big_Boss : constant Graph_Node := new Graph_Node_Record' (ID => 0, Dia_ID => To_Unbounded_String ("Ze Big Boss"), Position => Null_Point, Width => 0.0, Height => 0.0, BBox => Null_Rectangle, Parent => 0, Text => To_Unbounded_String (""), Text_Pos => Null_Point, Ports => null, Num_Ports => 0); GN : Graph_Node; E : Edge; Obj_Cpt : Natural := 1; Lnk_Cpt : Natural := 1; function Port_To_ID (P : Natural; NP : Natural) return Natural; function Port_To_ID (P : Natural; NP : Natural) return Natural is begin if P <= 2 * NP then return (P / 2) + 1; else return P + 1 - NP; end if; end Port_To_ID; begin Put_Line ("Translating XML to Graph"); Put_Line ("Number of Objects : " & Integer'Image (Num_Objects)); Put_Line ("Number of Links : " & Integer'Image (Num_Links)); -- We add an arbitrary root object, index 0, that contains -- every orphan node G.Nodes := new Graph_Node_Array (0 .. Num_Objects); G.Edges := new Adjacent_Graph (1 .. Num_Links); -- There are as many inclusion edges as objects (except the -- big one), because every object has exactly one parent G.Hierarchy := new Inclusion_Graph (1 .. Num_Objects); -- Filling in the graph... G.Nodes (0) := Big_Boss; for I in 0 .. Parser.Core.Nodes.Length (NL) - 1 loop Put_Line ("Creating node n° " & Integer'Image (I)); N := NL.Items (I); Num_Ports := Parser.Core.Nodes.Count_Ports (N); Num_Connections := Parser.Core.Nodes.Count_Connections (N); case N.Node_Type_Arg is when Parser.Core.AADLObject_Type => Put_Line ("=> This is object n° " & Integer'Image (Obj_Cpt)); if To_String (N.Parent) = "" then S := Big_Boss.Dia_ID; Nat := 0; else S := N.Parent; -- Works because a parent is always -- defined *before* a child for J in 0 .. Obj_Cpt - 1 loop declare N2 : constant Graph_Node := G.Nodes (J); begin if N2.Dia_ID = S then Nat := J; end if; end; end loop; end if; Ports := new Point_Array (1 .. (Num_Ports + Num_Connections)); for I in 1 .. Num_Ports loop Ports (I) := Null_Point; end loop; for I in 1 .. Num_Connections loop Ports (Num_Ports + I) := Null_Point; end loop; GN := new Graph_Node_Record' (ID => Obj_Cpt, Dia_ID => N.ID, Position => Null_Point, Width => 0.0, Height => 0.0, BBox => Null_Rectangle, Parent => Nat, Text => N.Name, Text_Pos => Null_Point, Ports => Ports, Num_Ports => Num_Ports); E := new Edge_Record' (Dia_ID => To_Unbounded_String (""), First => N.ID, First_Int => GN.ID, First_Port => -1, Second => S, Second_Int => Nat, Second_Port => -1); G.Nodes (Obj_Cpt) := GN; G.Hierarchy (Obj_Cpt) := E; Obj_Cpt := Obj_Cpt + 1; when Parser.Core.Link => Put_Line ("=> This is link n° " & Integer'Image (Lnk_Cpt)); E := new Edge_Record' (Dia_ID => N.ID, First => N.Conn.Start_Point.Object_ID, First_Int => 0, First_Port => N.Conn.Start_Point.Port, Second => N.Conn.End_Point.Object_ID, Second_Int => 0, Second_Port => N.Conn.End_Point.Port); G.Edges (Lnk_Cpt) := E; Lnk_Cpt := Lnk_Cpt + 1; when others => -- Will never happen null; end case; end loop; -- Resolve the integers in edges for I in 1 .. Num_Links loop E := G.Edges (I); for J in 1 .. Num_Objects loop GN := G.Nodes (J); if E.First = GN.Dia_ID then E.First_Int := J; E.First_Port := Port_To_ID (E.First_Port, GN.Num_Ports); end if; if E.Second = GN.Dia_ID then E.Second_Int := J; E.Second_Port := Port_To_ID (E.Second_Port, GN.Num_Ports); end if; end loop; end loop; return G; end XML_To_Graph; -- Updates a single dia:attribute procedure Update_Attribute (NL : in out DOM.Core.Node_List; Obj_Name : String; Attr_Name : String; Value : String) is use Ocarina.Dia; N2 : DOM.Core.Node; P : Natural := 0; Attrs : DOM.Core.Named_Node_Map; begin Put_Line ("> Updating attribute " & Obj_Name); N2 := Parser.Core.Attributes.Get_Attribute_Node (NL, Obj_Name); Parser.Core.Get_Required_Child (DOM.Core.Nodes.Child_Nodes (N2), Attr_Name, P, N2); Attrs := DOM.Core.Nodes.Attributes (N2); DOM.Core.Nodes.Set_Node_Value (DOM.Core.Nodes.Get_Named_Item (Attrs, "val"), Value); end Update_Attribute; -- Updates an arrow's connection points procedure Update_Arrow_Points (NL : in out DOM.Core.Node_List; Value1 : String; Value2 : String) is use Ocarina.Dia; N : DOM.Core.Node; N2 : DOM.Core.Node; P : Natural := 0; Attrs : DOM.Core.Named_Node_Map; begin Put_Line ("> Updating arrow points"); N := Parser.Core.Attributes.Get_Attribute_Node (NL, "conn_endpoints"); Parser.Core.Get_Required_Child (DOM.Core.Nodes.Child_Nodes (N), "dia:point", P, N2); Attrs := DOM.Core.Nodes.Attributes (N2); DOM.Core.Nodes.Set_Node_Value (DOM.Core.Nodes.Get_Named_Item (Attrs, "val"), Value1); P := P + 1; Parser.Core.Get_Required_Child (DOM.Core.Nodes.Child_Nodes (N), "dia:point", P, N2); Attrs := DOM.Core.Nodes.Attributes (N2); DOM.Core.Nodes.Set_Node_Value (DOM.Core.Nodes.Get_Named_Item (Attrs, "val"), Value2); end Update_Arrow_Points; -- Updates an object's ports' positions procedure Update_Ports (NL : in out DOM.Core.Node_List; Vect : Printer.Misc.Point; Values : Point_Array_Ptr) is use Ocarina.Dia; N : DOM.Core.Node; N2 : DOM.Core.Node; P : Natural := 0; Compteur : Natural := Values'First; NL2 : DOM.Core.Node_List; New_P : Point; begin Put_Line ("> Updating " & Integer'Image (Values'Last) & " ports"); N := Parser.Core.Attributes.Get_Attribute_Node (NL, "aadlbox_ports"); while Compteur <= Values'Last loop Parser.Core.Get_Required_Child (DOM.Core.Nodes.Child_Nodes (N), "dia:composite", P, N2); Put_Line ("Vect : " & Printer.Misc.Print_Point (Vect)); New_P := Values (Compteur); Put_Line ("RelPos : " & Printer.Misc.Print_Point (New_P)); New_P.X := New_P.X + Vect.X; New_P.Y := New_P.Y + Vect.Y; Put_Line ("AbsPos : " & Printer.Misc.Print_Point (New_P)); NL2 := DOM.Core.Nodes.Child_Nodes (N2); Update_Attribute (NL2, "point", "dia:point", Printer.Misc.Print_Point (New_P)); Compteur := Compteur + 1; P := P + 1; end loop; end Update_Ports; -- Updates an object's connection points procedure Update_Connection_Points (NL : in out DOM.Core.Node_List; Vect : Printer.Misc.Point; Values : Point_Array_Ptr) is use Ocarina.Dia; N : DOM.Core.Node; N2 : DOM.Core.Node; P : Natural := 0; Compteur : Natural := Values'First; Attrs : DOM.Core.Named_Node_Map; New_P : Point; begin Put_Line ("> Updating " & Integer'Image (Values'Last) & " connection points"); N := Parser.Core.Attributes.Get_Attribute_Node (NL, "aadlbox_connections"); while Compteur <= Values'Last loop Parser.Core.Get_Required_Child (DOM.Core.Nodes.Child_Nodes (N), "dia:point", P, N2); New_P := Values (Compteur); New_P.X := New_P.X + Vect.X; New_P.Y := New_P.Y + Vect.Y; Attrs := DOM.Core.Nodes.Attributes (N2); DOM.Core.Nodes.Set_Node_Value (DOM.Core.Nodes.Get_Named_Item (Attrs, "val"), Printer.Misc.Print_Point (New_P)); Compteur := Compteur + 1; P := P + 1; end loop; end Update_Connection_Points; -- Updates the XML Document with the new contents -- of the Graph (both must correspond) procedure Graph_To_XML (Doc : DOM.Core.Document; Graph : Compound_Graph) is use Ocarina.Dia; Document : constant DOM.Core.Node := DOM.Core.Documents.Get_Element (Doc); CL : DOM.Core.Node_List := DOM.Core.Nodes.Child_Nodes (Document); N : DOM.Core.Node; NL : DOM.Core.Node_List; P : Natural := 0; Id : Unbounded_String; Obj_Cpt : Natural := 1; Lnk_Cpt : Natural := 1; GN : Graph_Node; E : Edge; Pos : Point; T : Point; Rect : Rectangle; Absolute_Pos : array (0 .. Graph.Nodes'Last) of Point; Ports : Point_Array_Ptr; Connections : Point_Array_Ptr; begin Parser.Core.Get_Required_Child (CL, "dia:layer", P, N); CL := DOM.Core.Nodes.Child_Nodes (N); Put_Line ("Updating XML view from Graph..."); N := DOM.Core.Nodes.First_Child (N); Absolute_Pos (0) := (X => 0.0, Y => 0.0); while N /= null loop if Obj_Cpt <= Graph.Nodes'Last then GN := Graph.Nodes (Obj_Cpt); end if; if Lnk_Cpt <= Graph.Edges'Last then E := Graph.Edges (Lnk_Cpt); end if; if N.Node_Type = DOM.Core.Element_Node and then DOM.Core.Nodes.Node_Name (N) = "dia:object" then Put ("Updating object : "); NL := DOM.Core.Nodes.Child_Nodes (N); Id := To_Unbounded_String (Parser.Core.Get_Required_Attribute (DOM.Core.Nodes.Attributes (N), "id")); if Id = GN.Dia_ID then Put_Line ("node " & To_String (Id)); -- Computing the data Put_Line ("> Computing the data..."); T := Absolute_Pos (GN.Parent); Pos.X := GN.Position.X + T.X; Pos.Y := GN.Position.Y + T.Y; Absolute_Pos (Obj_Cpt) := (X => Pos.X, Y => Pos.Y); Rect.TL.X := GN.BBox.TL.X + T.X; Rect.TL.Y := GN.BBox.TL.Y + T.Y; Rect.BR.X := GN.BBox.BR.X + T.X; Rect.BR.Y := GN.BBox.BR.Y + T.Y; -- And dumping it Update_Attribute (NL, "obj_pos", "dia:point", Printer.Misc.Print_Point (Pos)); Update_Attribute (NL, "elem_corner", "dia:point", Printer.Misc.Print_Point (Pos)); Update_Attribute (NL, "elem_width", "dia:real", Float'Image (GN.Width)); Update_Attribute (NL, "elem_height", "dia:real", Float'Image (GN.Height)); Update_Attribute (NL, "obj_bb", "dia:rectangle", Printer.Misc.Print_Rectangle (Rect)); -- Translates all ports and connection points -- since they are bound the object Ports := new Point_Array (1 .. GN.Num_Ports); Connections := new Point_Array (1 .. GN.Ports'Last - GN.Num_Ports); for I in 1 .. GN.Num_Ports loop Ports (I) := GN.Ports (I); end loop; for I in 1 .. (GN.Ports'Last - GN.Num_Ports) loop Connections (I) := GN.Ports (GN.Num_Ports + I); end loop; Update_Ports (NL, Absolute_Pos (Obj_Cpt), Ports); Update_Connection_Points (NL, Absolute_Pos (Obj_Cpt), Connections); -- And last but not least, changing the textbox position -- Caution : overwrites the NL variable, so should be done -- last Put_Line ("> Updating textbox"); P := 0; declare N2 : DOM.Core.Node; begin N2 := Parser.Core.Attributes.Get_Attribute_Node (NL, "name"); Parser.Core.Get_Required_Child (DOM.Core.Nodes.Child_Nodes (N2), "dia:composite", P, N2); NL := DOM.Core.Nodes.Child_Nodes (N2); Pos.X := GN.Text_Pos.X + T.X; Pos.Y := GN.Text_Pos.Y + T.Y; Update_Attribute (NL, "pos", "dia:point", Printer.Misc.Print_Point (Pos)); exception when others => null; end; Obj_Cpt := Obj_Cpt + 1; else if Id = To_String (E.Dia_ID) then Put_Line ("link " & To_String (Id)); -- Computing the data Pos := Absolute_Pos (E.First_Int); T := Absolute_Pos (E.Second_Int); Pos.X := Pos.X + Graph.Nodes (E.First_Int).Ports (E.First_Port).X; Pos.Y := Pos.Y + Graph.Nodes (E.First_Int).Ports (E.First_Port).Y; T.X := T.X + Graph.Nodes (E.Second_Int).Ports (E.Second_Port).X; T.Y := T.Y + Graph.Nodes (E.Second_Int).Ports (E.Second_Port).Y; Rect.TL := Pos; Rect.BR := T; -- And putting it back into the tree Update_Attribute (NL, "obj_pos", "dia:point", Printer.Misc.Print_Point (Pos)); Update_Attribute (NL, "obj_bb", "dia:rectangle", Printer.Misc.Print_Rectangle (Rect)); Update_Arrow_Points (NL, Printer.Misc.Print_Point (Pos), Printer.Misc.Print_Point (T)); Lnk_Cpt := Lnk_Cpt + 1; else Put ("Graph_To_XML has been called"); Put_Line (" on incompatible arguments."); raise Incompatible_Arguments; end if; end if; end if; N := DOM.Core.Nodes.Next_Sibling (N); end loop; end Graph_To_XML; -- Optimization of the graph layout procedure Optimize (Graph : in out Compound_Graph) is begin Optimize_Node (Graph, Graph.Nodes'First); end Optimize; procedure Optimize_Node (Graph : in out Compound_Graph; I : Natural) is P : Edge; N : Natural := 0; List_Childs : List_Nat := null; Is_Child : Boolean_Array (Graph.Nodes'First .. Graph.Nodes'Last); Is_Child2 : Boolean_Array (Graph.Nodes'First .. Graph.Nodes'Last); Num_Child : Natural_Array (Graph.Nodes'First .. Graph.Nodes'Last); begin -- Compute the BBox, width and height of the Childs for J in Is_Child'First .. Is_Child'Last loop Is_Child (J) := False; Is_Child2 (J) := False; Num_Child (J) := 0; end loop; -- Put_Line ("Optimization of " & To_String (Graph.Nodes (I).Dia_ID)); for J in Graph.Hierarchy'First .. Graph.Hierarchy'Last loop P := Graph.Hierarchy (J); if I = P.Second_Int then Optimize_Node (Graph, P.First_Int); Add (List_Childs, P.First_Int); Is_Child (P.First_Int) := True; Is_Child2 (P.First_Int) := True; N := N + 1; Num_Child (P.First_Int) := N; end if; end loop; -- Put_Line (Natural'Image (N) & " childrens"); -- Put the childs at a right place. declare R_Width : Float := 0.0; R_Height : Float := 0.0; L : List_Nat := List_Childs; T : Graph_Node; X : Float; Y : Float; Current : Natural; Value : Float_Array (1 .. N); -- L_One_Neighbour_Set : List_Nat := null; Occurences : Float_Array (1 .. N); Signe : Float := 1.0; begin -- Compute the length of the axes of the ellipse while L /= null loop T := Graph.Nodes (L.H); R_Width := R_Width + T.Width; R_Height := R_Height + T.Height; L := L.Q; end loop; -- Put_Line ("ellipse axes computed"); L := Copy_List (List_Childs); -- Compute the value of the Childs. for J in 1 .. N loop Value (J) := 0.0; Occurences (J) := 0.0; end loop; -- At this point, Value is the max of connections for J in Graph.Edges'First .. Graph.Edges'Last loop if Is_Child (Graph.Edges (J).First_Int) then if Is_Child (Graph.Edges (J).Second_Int) then Value (Num_Child (Graph.Edges (J).Second_Int)) := Value (Num_Child (Graph.Edges (J).Second_Int)) + 1.0; Value (Num_Child (Graph.Edges (J).First_Int)) := Value (Num_Child (Graph.Edges (J).First_Int)) + 1.0; end if; end if; end loop; -- Put_Line ("value of Child computed"); if L /= null then -- first position R_Height := (R_Height / 6.0) + (0.75 * Float (N)); R_Width := (R_Width / 4.0) + (0.75 * Float (N)); Put_Line ("RW : " & Float'Image (R_Width) & " RH : " & Float'Image (R_Height)); X := 2.0; Y := R_Height * (1.0 + Signe * Sqrt ((1.0 - Pow ((X - 2.0) / R_Width - 1.0, 2)))); -- We place the Child of highest value. Current := Highest (Value); Put_Line ("value : " & Float'Image (Value (Current))); Current := Element_Index (List_Childs, N - Current); Remove (L, Current); Is_Child (Current) := False; Value (Current) := -1.0; Graph.Nodes (Current).Position.X := X; Graph.Nodes (Current).Position.Y := Y; -- debug -- Put_Line ("Position of " & -- To_String (Graph.Nodes (Current).Dia_ID) & -- " : " & Float'Image (X) & " , " & Float'Image (Y)); X := X + Signe * ((Graph.Nodes (Current).Width) + 3.0); -- fin debug if X > (2.0 * R_Width + 2.0) then Signe := -Signe; X := 2.0 * R_Width; end if; Y := R_Height * (1.0 + Signe * Sqrt ((1.0 - Pow ((X - 2.0) / R_Width - 1.0, 2)))); -- Put_Line ("First Child placed"); end if; while L /= null loop for J in 1 .. N loop Occurences (J) := 0.0; end loop; declare A : Natural; B : Natural; -- Compute the neighbours of List_Chides.H -- and the number of connections between -- them. begin for J in Graph.Edges'First .. Graph.Edges'Last loop A := Graph.Edges (J).First_Int; B := Graph.Edges (J).Second_Int; if Is_Child (A) then A := Num_Child (A); if B = Current then Occurences (A) := Occurences (A) + 1.0; end if; end if; if Is_Child (B) then B := Num_Child (B); if A = Current then Occurences (B) := Occurences (B) + 1.0; end if; end if; end loop; end; -- Select the next neighbour. Current := Highest (Occurences); -- Put_Line ("occurences : " & Float'Image -- (Occurences (Current))); if Occurences (Current) = 0.0 then Current := Highest (Value); end if; -- Put_Line ("value : " & Float'Image (Value (Current))); Value (Current) := -1.0; Current := Element_Index (List_Childs, N - Current); -- TODO : chose a better neigbour (using Value AND Occurences) -- Place the next neighbour. Remove (L, Current); Is_Child (Current) := False; if Signe < 0.0 then X := X + Signe * ((Graph.Nodes (Current).Width) + 3.0); if X < 2.0 then X := 2.0; end if; end if; Graph.Nodes (Current).Position.X := X; Y := R_Height * (1.0 + Signe * Sqrt ((1.0 - Pow ((X - 2.0) / R_Width - 1.0, 2)))); if Signe < 0.0 then Y := Y + Graph.Nodes (Current).Height + 1.0; end if; Graph.Nodes (Current).Position.Y := Y; -- Put_Line ("Position of " & -- To_String (Graph.Nodes (Current).Dia_ID) & -- " : " & Float'Image (X) & " , " & Float'Image (Y)); if Signe > 0.0 then X := X + Signe * ((Graph.Nodes (Current).Width) + 3.0); if X > (2.0 * R_Width + 2.0) then Signe := -Signe; X := 2.0 * R_Width + 2.0; end if; end if; -- if Signe < 0.0 then -- X := X - 3.0; -- end if; end loop; end; -- Compute the BBox, width and height of Node I declare Width_Max : Float; Height_Max : Float; W : Float; H : Float; L : List_Nat := List_Childs; T : Graph_Node; begin Width_Max := 0.0; Height_Max := 0.0; while L /= null loop T := Graph.Nodes (L.H); W := T.Position.X + T.Width; H := T.Position.Y + T.Height; if W > Width_Max then Width_Max := W; end if; if H > Height_Max then Height_Max := H; end if; L := L.Q; end loop; Width_Max := Width_Max + 4.0; Height_Max := Height_Max + 2.0; -- Warnings : check if these values are correct Graph.Nodes (I).Width := Max (Width_Max, 10.0); Graph.Nodes (I).Height := Height_Max; Graph.Nodes (I).BBox := ((0.0, 0.0), (Width_Max, Height_Max)); -- TODO : free List_Childs -- Free_List (List_Childs); -- TODO : place connextions and ports end; if Graph.Nodes (I).Ports /= null then for J in Graph.Nodes (I).Ports'First .. Graph.Nodes (I).Ports'Last loop Graph.Nodes (I).Ports (J) := (0.0, 0.0); end loop; end if; declare Pos1 : Boolean; Pos2 : Boolean; P1 : Point; P2 : Point; P3 : Point; A : Natural; B : Natural; begin for J in Graph.Edges'First .. Graph.Edges'Last loop A := Graph.Edges (J).First_Int; B := Graph.Edges (J).Second_Int; P1 := (Graph.Nodes (A).Position.X, Graph.Nodes (A).Position.Y + Graph.Nodes (A).Height); P2 := (Graph.Nodes (A).Position.X + Graph.Nodes (A).Width, Graph.Nodes (A).Position.Y); P3 := (Graph.Nodes (A).Position.X + Graph.Nodes (A).Width, Graph.Nodes (A).Position.Y + Graph.Nodes (A).Height); if Is_Child2 (A) and then Is_Child2 (B) then Pos1 := Is_Up (Graph.Nodes (A).Position, P3, Graph.Nodes (B).Position); Pos2 := Is_Up (P1, P2, Graph.Nodes (B).Position); Put_Line ("A" & Natural'Image (A)); Put_Line ("B" & Natural'Image (B)); Put_Line ("A1" & Natural'Image (Graph.Edges (J).First_Port)); Put_Line ("B1" & Natural'Image (Graph.Edges (J).Second_Port)); if Pos1 then if Pos2 then -- Bottom Graph.Nodes (A).Ports (Graph.Edges (J).First_Port) := (Graph.Nodes (A).Width / 2.0, Graph.Nodes (A).Height); Graph.Nodes (B).Ports (Graph.Edges (J).Second_Port) := (Graph.Nodes (B).Width / 2.0, 0.0); else -- Left Graph.Nodes (A).Ports (Graph.Edges (J).First_Port) := (0.0, Graph.Nodes (A).Height / 2.0); Graph.Nodes (B).Ports (Graph.Edges (J).Second_Port) := (Graph.Nodes (B).Width, Graph.Nodes (B).Height / 2.0); end if; else if Pos2 then -- Right Graph.Nodes (A).Ports (Graph.Edges (J).First_Port) := (Graph.Nodes (A).Width, Graph.Nodes (A).Height / 2.0); Graph.Nodes (B).Ports (Graph.Edges (J).Second_Port) := (0.0, Graph.Nodes (B).Height / 2.0); else -- Up Graph.Nodes (A).Ports (Graph.Edges (J).First_Port) := (Graph.Nodes (A).Width / 2.0, 0.0); Graph.Nodes (B).Ports (Graph.Edges (J).Second_Port) := (Graph.Nodes (B).Width / 2.0, Graph.Nodes (B).Height); end if; end if; end if; if Is_Child2 (A) and then not Is_Child2 (B) then if Graph.Nodes (A).Position.Y > Graph.Nodes (B).Height / 2.0 then if Graph.Nodes (B).Ports (Graph.Edges (J).Second_Port).X = 0.0 and then Graph.Nodes (B).Ports ( Graph.Edges (J).Second_Port).Y = 0.0 then Graph.Nodes (B).Ports (Graph.Edges (J).Second_Port) := (Graph.Nodes (A).Position.X + Graph.Nodes (A).Width / 2.0, Graph.Nodes (B).Height); end if; if Graph.Nodes (A).Ports (Graph.Edges (J).First_Port).X = 0.0 and then Graph.Nodes (A).Ports (Graph.Edges (J).First_Port) .Y = 0.0 then Graph.Nodes (A).Ports (Graph.Edges (J).First_Port) := (Graph.Nodes (A).Width / 2.0, Graph.Nodes (A).Height); end if; else if Graph.Nodes (B).Ports (Graph.Edges (J).Second_Port).X = 0.0 and then Graph.Nodes (B).Ports ( Graph.Edges (J).Second_Port).Y = 0.0 then Graph.Nodes (B).Ports (Graph.Edges (J).Second_Port) := (Graph.Nodes (A).Position.X + Graph.Nodes (A).Width / 2.0, 0.0); end if; if Graph.Nodes (A).Ports (Graph.Edges (J).First_Port).X = 0.0 and then Graph.Nodes (A).Ports (Graph.Edges (J).First_Port) .Y = 0.0 then Graph.Nodes (A).Ports (Graph.Edges (J).First_Port) := (Graph.Nodes (A).Width / 2.0, 0.0); end if; end if; end if; if Is_Child2 (B) and then not Is_Child2 (A) then if Graph.Nodes (B).Position.Y > Graph.Nodes (A).Height / 2.0 then if Graph.Nodes (A).Ports (Graph.Edges (J).Second_Port).X = 0.0 and then Graph.Nodes (A).Ports ( Graph.Edges (J).Second_Port).Y = 0.0 then Graph.Nodes (A).Ports (Graph.Edges (J).Second_Port) := (Graph.Nodes (B).Position.X + Graph.Nodes (B).Width / 2.0, Graph.Nodes (A).Height); end if; if Graph.Nodes (B).Ports (Graph.Edges (J).First_Port).X = 0.0 and then Graph.Nodes (B).Ports (Graph.Edges (J).First_Port) .Y = 0.0 then Graph.Nodes (B).Ports (Graph.Edges (J).First_Port) := (Graph.Nodes (B).Width / 2.0, Graph.Nodes (B).Height); end if; else if Graph.Nodes (A).Ports (Graph.Edges (J).Second_Port).X = 0.0 and then Graph.Nodes (A).Ports ( Graph.Edges (J).Second_Port).Y = 0.0 then Graph.Nodes (A).Ports (Graph.Edges (J).Second_Port) := (Graph.Nodes (B).Position.X + Graph.Nodes (B).Width / 2.0, 0.0); end if; if Graph.Nodes (B).Ports (Graph.Edges (J).First_Port).X = 0.0 and then Graph.Nodes (B).Ports (Graph.Edges (J).First_Port) .Y = 0.0 then Graph.Nodes (B).Ports (Graph.Edges (J).First_Port) := (Graph.Nodes (B).Width / 2.0, 0.0); end if; end if; end if; end loop; end; end Optimize_Node; procedure Add (L : in out List_Nat; I : in Integer) is begin L := new List_Element'(I, L); end Add; function Is_Up (P : Point; P1 : Point; P2 : Point) return Boolean is T : Float; begin T := (P1.X - P.X) * (P2.Y - P.Y) - (P1.Y - P.Y) * (P2.X - P.X); Put_Line (Float'Image (T)); if T > 0.0 then return True; else return False; end if; end Is_Up; function Highest (A : Float_Array) return Natural is T : Float := -1.0; I : Natural := 1; begin for J in A'First .. A'Last loop -- Put_Line (Float'Image (A (J))); if A (J) > T then T := A (J); I := J; end if; end loop; return I; end Highest; function Max (A : Float; B : Float) return Float is begin if A > B then return A; else return B; end if; end Max; function Copy_List (L : List_Nat) return List_Nat is begin if L = null then return null; else return new List_Element'(L.H, Copy_List (L.Q)); end if; end Copy_List; procedure Remove (L : in out List_Nat; I : Integer) is begin if L /= null then if L.H = I then L := L.Q; else Remove (L.Q, I); end if; end if; end Remove; function Element_Index (L : List_Nat; I : Integer) return Natural is begin if L /= null then if I = 0 then return L.H; else return Element_Index (L.Q, I - 1); end if; end if; Put_Line ("Element Not Found"); return Natural'Last; end Element_Index; function Pow (F : Float; I : Integer) return Float is begin if I = 0 then return 1.0; else return F * Pow (F, I - 1); end if; end Pow; -- procedure Free_List (L : in out List_Nat) is -- begin -- if L /= null then -- Free_List (L.Q); -- Free (L); -- end if; -- end Free_List; -- (Debug) Prints a graph to the stdo procedure Dump_Graph (G : Compound_Graph) is GN : Graph_Node; E : Edge; begin Put_Line ("##########################"); Put_Line ("Dumping graph begins... "); Put_Line ("Graph Nodes :"); for I in 1 .. G.Nodes'Last loop GN := G.Nodes (I); Put_Line ("-----------------------"); Put_Line ("Node n° " & Natural'Image (GN.ID)); Put_Line ("Dia_ID : " & To_String (GN.Dia_ID)); Put_Line ("Position : " & Print_Point (GN.Position)); Put_Line ("Width : " & Float'Image (GN.Width) & " Height : " & Float'Image (GN.Height)); Put_Line ("Bounding Box : " & Print_Rectangle (GN.BBox)); Put_Line ("Parent : " & Natural'Image (GN.Parent)); Put_Line ("Text : " & To_String (GN.Text) & " at " & Print_Point (GN.Position)); Put_Line ("Number of ports :" & Natural'Image (GN.Num_Ports)); Put_Line ("Number of connections :" & Natural'Image (GN.Ports'Length - GN.Num_Ports)); end loop; Put_Line (""); Put_Line ("Edges :"); for I in 1 .. G.Edges'Last loop E := G.Edges (I); Put_Line ("-----------------------"); Put_Line ("Edge n° " & Natural'Image (I)); Put_Line ("Dia_ID : " & To_String (E.Dia_ID)); Put_Line ("Start : " & To_String (E.First) & ", n° " & Natural'Image (E.First_Int) & " at anchor n° " & Integer'Image (E.First_Port) & " located at " & Print_Point (G.Nodes (E.First_Int).Ports (E.First_Port))); Put_Line ("End : " & To_String (E.Second) & ", n° " & Natural'Image (E.Second_Int) & " at anchor n° " & Integer'Image (E.Second_Port) & " located at " & Print_Point (G.Nodes (E.Second_Int).Ports (E.Second_Port))); end loop; Put_Line ("###########################"); end Dump_Graph; end Ocarina.Dia.Printer.Optimize;