(* Copyright 1992 Digital Equipment Corporation. *) (* Distributed only by permission. *) (* Last modified on Thu Jan 14 08:05:17 PST 1993 by steveg *) (* modified on Fri Aug 7 07:15:28 PDT 1992 by mhb *) MODULE AnimationPath; IMPORT GraphVBT, List, R2; REVEAL StraightPath = StraightPathPublic BRANDED OBJECT p0, p1: R2.T; OVERRIDES init := StraightInit; pos := StraightPos; END; PROCEDURE StraightInit (self: StraightPath; p0, p1: R2.T): T = BEGIN self.p0 := p0; self.p1 := p1; RETURN self END StraightInit; PROCEDURE StraightPos (self: StraightPath; t: REAL): R2.T = BEGIN RETURN R2.Add(self.p0, R2.Scale(t, R2.Sub(self.p1, self.p0))) END StraightPos; REVEAL BezierPath = BezierPathPublic BRANDED OBJECT p0, p1, p2, p3: R2.T; OVERRIDES init := BezierInit; pos := BezierPos; END; PROCEDURE BezierInit (self: BezierPath; p0, p1, p2, p3: R2.T):T = BEGIN self.p0 := p0; self.p1 := p1; self.p2 := p2; self.p3 := p3; RETURN self END BezierInit; PROCEDURE BezierPos (self: BezierPath; t: REAL): R2.T = BEGIN RETURN R2.T{ Cubic(self.p0[0], self.p1[0], self.p2[0], self.p3[0], t), Cubic(self.p0[1], self.p1[1], self.p2[1], self.p3[1], t)} END BezierPos; REVEAL EdgePath = EdgePathPublic BRANDED OBJECT e: GraphVBT.Edge OVERRIDES init := EdgeInit; pos := EdgePos; END; PROCEDURE EdgeInit (self: EdgePath; e: GraphVBT.Edge): T = BEGIN self.e := e; RETURN self END EdgeInit; PROCEDURE EdgePos (self: EdgePath; t: REAL): R2.T = VAR p0, p1, p2, p3: R2.T; BEGIN LOCK self.e.control0.graph.mu DO IF (self.e.control0 = NIL) OR (self.e.control1 = NIL) THEN p0 := self.e.vertex0.pos; p1 := self.e.vertex1.pos; RETURN R2.Mix(p0, 1.0 - t, p1, t); ELSE p0 := self.e.vertex0.pos; p1 := self.e.control0.pos; p2 := self.e.control1.pos; p3 := self.e.vertex1.pos; RETURN R2.T{Cubic(p0[0], p1[0], p2[0], p3[0], t), Cubic(p0[1], p1[1], p2[1], p3[1], t)} END; END; END EdgePos; PROCEDURE Cubic (c0, c1, c2, c3: REAL; t: REAL): REAL = BEGIN RETURN c0 + t * (3.0 * (c1 - c0) + t * (3.0 * ((c2 - c1) - (c1 - c0)) + t * ((c3 - c0) - 3.0 * (c2 - c1)))) END Cubic; TYPE Edges = REF ARRAY OF RECORD e: GraphVBT.Edge; l: REAL END; REVEAL MultipleEdgePath = MultipleEdgePathPublic BRANDED OBJECT edges: Edges; totalLength: REAL; OVERRIDES init := MultipleEdgeInit; pos := MultipleEdgePos; END; PROCEDURE MultipleEdgeInit (self: MultipleEdgePath; edges: List.T): T = VAR total := 0.0; BEGIN self.edges := NEW(Edges, List.Length(edges)); FOR i := 0 TO LAST(self.edges^) DO WITH e = List.Pop(edges), l = EdgeLength(e) DO self.edges[i].e := e; self.edges[i].l := l; total := total + l; END; END; self.totalLength := total; RETURN self END MultipleEdgeInit; PROCEDURE EdgeLength (e: GraphVBT.Edge): REAL = BEGIN IF e.control0 = NIL THEN RETURN R2.Length(R2.Sub(e.vertex0.pos, e.vertex1.pos)) ELSE RETURN R2.Length(R2.Sub(e.vertex0.pos, e.control0.pos)) + R2.Length(R2.Sub(e.control0.pos, e.control1.pos)) + R2.Length(R2.Sub(e.control1.pos, e.vertex1.pos)) END; END EdgeLength; PROCEDURE MultipleEdgePos (self: MultipleEdgePath; t: REAL): R2.T = VAR p0, p1, p2, p3: R2.T; total, goal : REAL := 0.0; e : GraphVBT.Edge; BEGIN LOCK self.edges[0].e.vertex0.graph.mu DO goal := t * self.totalLength; FOR i := 0 TO LAST(self.edges^) DO WITH l = self.edges[i].l DO IF total + l >= goal THEN e := self.edges[i].e; t := (goal - total) / l; EXIT; END; total := total + l; END; END; IF (e.control0 = NIL) OR (e.control1 = NIL) THEN p0 := e.vertex0.pos; p1 := e.vertex1.pos; RETURN R2.Mix(p0, 1.0 - t, p1, t); ELSE p0 := e.vertex0.pos; p1 := e.control0.pos; p2 := e.control1.pos; p3 := e.vertex1.pos; RETURN R2.T{Cubic(p0[0], p1[0], p2[0], p3[0], t), Cubic(p0[1], p1[1], p2[1], p3[1], t)} END; END; END MultipleEdgePos; BEGIN END AnimationPath.