# In various places we consider points in the fundamental domain
# of a cromulent surface, which may be permitted to move.
# Some points may be constrained to lie on one of the four sides
# of the domain, which are the curves C[0], C[1], C[3] and C[5].
# Other points may be constrained to lie at one of the four
# corners, which are v[0], v[3], v[6] and v[11].  We use numerical
# codes for these cases, as follows:

#@ CONSTRAINT
CONSTRAINT["FREE"]  := 0:
CONSTRAINT["C1"]    := 2:
CONSTRAINT["C3"]    := 4:
CONSTRAINT["C5"]    := 5:
CONSTRAINT["C0"]    := 8:
CONSTRAINT["FIXED"] := 15:
CONSTRAINT["V0"]    := 15:
CONSTRAINT["V3"]    := 31:
CONSTRAINT["V6"]    := 47:
CONSTRAINT["V11"]   := 63:

# The following block sets things up so that constraint_name[31]
# (for example) is the string "CONSTRAINT_V3".

#@ constraint_name 
constraint_name := table():

proc()
 local i,n;
 global constraint_name;

 for i in map(op,[indices(CONSTRAINT)]) do 
  n := cat("CONSTRAINT_",i);
  assign(convert(n,name),CONSTRAINT[i]);
  constraint_name[CONSTRAINT[i]] := i;
 od:
end():

#@ curve_index_by_constraint 
curve_index_by_constraint := table():
curve_index_by_constraint[CONSTRAINT_FREE]  := NULL:
curve_index_by_constraint[CONSTRAINT_C1]    := 1:
curve_index_by_constraint[CONSTRAINT_C3]    := 3:
curve_index_by_constraint[CONSTRAINT_C5]    := 5:
curve_index_by_constraint[CONSTRAINT_C0]    := 0:
curve_index_by_constraint[CONSTRAINT_FIXED] := NULL:
curve_index_by_constraint[CONSTRAINT_V0]    := NULL:
curve_index_by_constraint[CONSTRAINT_V3]    := NULL:
curve_index_by_constraint[CONSTRAINT_V6]    := NULL:
curve_index_by_constraint[CONSTRAINT_V11]   := NULL:

#@ constraint_colour 
constraint_colour := table():
constraint_colour[CONSTRAINT_FREE]  := black:
constraint_colour[CONSTRAINT_C1]    := c_colour[1]:
constraint_colour[CONSTRAINT_C3]    := c_colour[3]:
constraint_colour[CONSTRAINT_C5]    := c_colour[5]:
constraint_colour[CONSTRAINT_C0]    := c_colour[0]:
constraint_colour[CONSTRAINT_FIXED] := black:
constraint_colour[CONSTRAINT_V0]    := black:
constraint_colour[CONSTRAINT_V3]    := black:
constraint_colour[CONSTRAINT_V6]    := black:
constraint_colour[CONSTRAINT_V11]   := black:

#@ constraint_table 
constraint_table := [
 [CONSTRAINT_V6,CONSTRAINT_C1,CONSTRAINT_V0],
 [CONSTRAINT_C0,CONSTRAINT_FREE,CONSTRAINT_C5],
 [CONSTRAINT_V3,CONSTRAINT_C3,CONSTRAINT_V11]
]:

#@ constraint_for_square 
constraint_for_square := proc(x,y)
 local i,j;

 i := `if`(x = 0,1,`if`(x = 1,3,2));
 j := `if`(y = 0,1,`if`(y = 1,3,2));
 return constraint_table[j][i];
end:

######################################################################

#@ CLASS: domain
`Class/Declare`("domain",
 "An instance represents the fundamental domain in a cromulent surface",
 
 ["Field","type"::string = "abstract",
  "A string describing the type of domain (e.g. @E@ for a member of the embedded family)"
 ],

 ["Field","dim"::posint,
  "All domains are expected to be subsets of $\\mathbb{R}^d$ for some integer $d$, stored in this field."
 ],

 ["Field","point_class_name",
  "Points in the domain will be represented by instances of a subclass of the abstract class @domain_point@.  The name of the relevant subclass is stored in this field, and the table representing the subclass is stored in the @point_class@ field.  Both fields should be set using the @set_point_class@ method.  Analogous comments apply to the fields @edge_class_name@, @edge_class@, @face_class_name@, @face_class@, @grid_class_name@ and @grid_class@."
 ],

 ["Field","point_class",
  ""
 ],

 ["Field","edge_class_name"],
 ["Field","edge_class"],
 ["Field","face_class_name"],
 ["Field","face_class"],
 ["Field","grid_class_name"],
 ["Field","grid_class"],

 ["Constructor","",
  proc(this)
   this["point_class_name"] := "domain_point";
   this["edge_class_name" ] := "domain_edge";
   this["face_class_name"]  := "domain_face";
   this["grid_class_name"]  := "domain_grid";
   this["point_class"] := eval(`class/domain_point`);
   this["edge_class" ] := eval(`class/domain_edge`);
   this["face_class"]  := eval(`class/domain_face`);
   this["grid_class"]  := eval(`class/domain_grid`);
  end
 ],

 ["Method","set_point_class"::void,"",
  proc(this,classname::string)
   this["point_class_name"] := classname;
   this["point_class"] := eval(convert(cat("class/",classname),name));
   NULL;
  end
 ],

 ["Method","set_edge_class"::void,"",
  proc(this,classname::string)
   this["edge_class_name"] := classname;
   this["edge_class"] := eval(convert(cat("class/",classname),name));
   NULL;
  end
 ],

 ["Method","set_face_class"::void,"",
  proc(this,classname::string)
   this["face_class_name"] := classname;
   this["face_class"] := eval(convert(cat("class/",classname),name));
   NULL;
  end
 ],

 ["Method","set_grid_class"::void,"",
  proc(this,classname::string)
   this["grid_class_name"] := classname;
   this["grid_class"] := eval(convert(cat("class/",classname),name));
   NULL;
  end
 ],

 ["Method","new_point"::void,
  "This method returns a new instance of the relevant point class.",
  proc(this)
   eval(this["point_class"]["FullConstructor"]());
  end
 ],

 ["Method","new_edge"::void,
  "This method returns a new instance of the relevant edge class.",
  proc(this,P0,P1)
   eval(this["edge_class"]["FullConstructor"](P0,P1));
  end
 ],

 ["Method","new_face"::void,
  "This method returns a new instance of the relevant face class.",
  proc(this,S0,S1,S2,s_)
   eval(this["face_class"]["FullConstructor"](args[2..-1]));
  end
 ],

 ["Method","new_grid"::void,
  "This method returns a new instance of the relevant grid class.",
  proc(this)
   local G;

   G := eval(this["grid_class"]["FullConstructor"]());
   G["domain"] := eval(this);
   return eval(G);
  end
 ],

 NULL
);