public Tuple <double, double> IntervalIntersection(ClipSchema <DiscourseAsset> da, ClipSchema <FabulaAsset> fa) { double start = 0; double end = 0; // do they overlap at all? if (da.start + da.duration < fa.start) { throw new System.Exception(); } if (fa.start + fa.duration < da.start) { throw new System.Exception(); } // the interval is the last one to start and the first one to end. if (da.start < fa.start) { start = fa.start; } else { start = da.start; } if (da.start + da.duration <= fa.start + fa.duration) { return(new Tuple <double, double>(start, da.start + da.duration)); } else { return(new Tuple <double, double>(start, fa.start + fa.duration)); } }
public void Read() { Terms = new List <ITerm>(); fabulaClips = new List <ClipSchema <FabulaAsset> >(); discourseClips = new List <ClipSchema <DiscourseAsset> >(); globalConstraints = new List <ClipSchema <ConstraintAsset> >(); // get params by perceiving playable director. for (int i = 0; i < objectParameterTypes.Count; i++) { var newTerm = new Term(i.ToString() + "_unboundTerm"); newTerm.Type = objectParameterTypes[i].name; Terms.Add(newTerm as ITerm); } var timelineasset = playableDirector.playableAsset as TimelineAsset; foreach (var track in timelineasset.GetRootTracks()) { if (track.name.Equals("ConstraintTrack")) { foreach (var clip in track.GetClips()) { var cschema = new ClipSchema <ConstraintAsset>(clip.start, clip.duration, clip.displayName, clip.asset as ConstraintAsset); globalConstraints.Add(cschema); } } if (track.name.Equals("FabulaTrack")) { foreach (var clip in track.GetClips()) { var cschema = new ClipSchema <FabulaAsset>(clip.start, clip.duration, clip.displayName, clip.asset as FabulaAsset); fabulaClips.Add(cschema); } } if (track.name.Equals("DiscourseTrack")) { foreach (var clip in track.GetClips()) { var cschema = new ClipSchema <DiscourseAsset>(clip.start, clip.duration, clip.displayName, clip.asset as DiscourseAsset); discourseClips.Add(cschema); UpdateActionSegmentsWithPercentages(cschema); } } } }
public Tuple <double, double> PercentIntersect(Tuple <double, double> intervalX, ClipSchema <FabulaAsset> fa) { double percentStart = 0; double percentEnd = 1; if (intervalX.First >= fa.start) { // The percentage of intervalX.First into fa.duration percentStart = (intervalX.First - fa.start) / fa.duration; } if (intervalX.Second < fa.start + fa.duration) { // The percentage of fa.Second into fa.duration percentEnd = (fa.start + fa.duration - intervalX.Second) / fa.duration; } return(new Tuple <double, double>(Math.Round(percentStart, 2), Math.Round(percentEnd, 2))); }
public void UpdateActionSegmentsWithPercentages(ClipSchema <DiscourseAsset> discClip) { Tuple <double, double> percents; ClipSchema <FabulaAsset> lastClip = null; List <int> placesToInsertFreeSpaceActionSeg = new List <int>(); for (int i = 0; i < discClip.asset.targetSchema.ActionSegs.Count; i++) //foreach (var actionSeg in discClip.asset.targetSchema.ActionSegs) { var actionSeg = discClip.asset.targetSchema.ActionSegs[i]; if (actionSeg.actionVarName == "") { // this actionSeg is just free space. continue; } bool notFound = true; ClipSchema <FabulaAsset> fabulaClip = null; foreach (var clip in fabulaClips) { // find the fabula clip associated and read in the percent values of the segment. (Later, these can be rounded?) if (clip.display.Equals(actionSeg.actionVarName)) { // This is the clip we were looking for fabulaClip = clip; // What's the intersection between the discourse clip and the fabula clip? var intersect = IntervalIntersection(discClip, clip); percents = PercentIntersect(intersect, clip); actionSeg.startPercent = percents.First; actionSeg.endPercent = percents.Second; // We've found this so we can stop searching for clips notFound = false; break; } } if (notFound) { Debug.Log("couldn't find referenced fabula action in clip step: " + discClip.asset.name); // then we didn't find the clip referenced. throw new System.Exception(); } if (lastClip != null) { // compare last clip to fabula clip. If they are consecutive, then there is no free space. if (lastClip.start + lastClip.duration < (fabulaClip.start - 0.06)) { // there is free space. placesToInsertFreeSpaceActionSeg.Add(i); } } lastClip = fabulaClip; } int numberOfInsertions = 0; foreach (var index in placesToInsertFreeSpaceActionSeg) { discClip.asset.targetSchema.ActionSegs.Insert(index + numberOfInsertions++, new ActionSeg()); } }
public CamPlanStep ReadCamStepVariable(ClipSchema <DiscourseAsset> discClip) { /* Input: the ClipSchema<DiscourseAsset> * Output: A camera plan step (type CamPlanStep) representing a single durative camera action/shot * * CamPlanStep references * A specific Camera GameObject (cloned) * A specific target transform to focus on * A set of action segments which can be used to specify duration */ // Now doing this in Read() //UpdateActionSegmentsWithPercentages(discClip); // The Terms of the camera plan step are just its scale, hangle, vangle, and the location of the target. var termList = new List <ITerm>() { new Term(discClip.asset.camSchema.scale.ToString(), true) as ITerm, new Term(discClip.asset.camSchema.hangle.ToString(), true) as ITerm, new Term(discClip.asset.camSchema.vangle.ToString(), true) as ITerm, new Term(discClip.asset.camSchema.targetLocation.ToString(), true) as ITerm }; // include here, other constraints that can be tacked on, like horizontal and vertical damping, amount of lead time, etc. Can we just display the entire cinemachine camera body? // Since there's a list of action segments, specifying just one is not good. // new Term(actionSeg.actionVarName.ToString(), true) as ITerm, // new Term(actionSeg.startPercent.ToString(), true) as ITerm, // new Term(actionSeg.endPercent.ToString(), true) as ITerm //}; foreach (var constraint in discClip.asset.Constraints) { var constraintParts = constraint.Split(' '); // depreciated - specific object targets would be specified in target details (CamTargetSchema) //if (constraintParts[0].Equals("target")) //{ // termList.Add(new Term(constraintParts[1], true) as ITerm); //} // special constraints for different kinds of targets? Here, would create a new target and perhaps merge 2 targets? if (constraintParts[0].Equals("2-shot")) { // create new target here? } // TODO: do something with them. so far, unneeded... } // Create the CameraPlanStep CamPlanStep ps; var root = new Operator(new Predicate("", termList, true), new List <IPredicate>(), new List <IPredicate>()); ps = new CamPlanStep(root as IOperator); // Assign updated target Schemata ps.TargetDetails = discClip.asset.targetSchema; // Assign CamSchema, will be used to filter valid camera objects (CamAttributesStruct) ps.CamDetails = discClip.asset.camSchema; return(ps); }
public PlanStep ReadStepVariable(ClipSchema <FabulaAsset> schema) { PlanStep ps; FabulaAsset fabAsset = schema.asset; var terms = new List <ITerm>(); var preconditions = new List <IPredicate>(); var effects = new List <IPredicate>(); string schemaName = ""; var unityactionschema = fabAsset.schema; if (unityactionschema != null) { schemaName = unityactionschema.name; var actionOperator = GameObject.Find(schemaName).GetComponent <UnityActionOperator>(); for (int i = 0; i < actionOperator.MutableParameters.Count; i++) { var newTerm = new Term(i.ToString()) { Type = actionOperator.MutableParameters[i].name }; terms.Add(newTerm as ITerm); } } foreach (var constraint in fabAsset.Constraints) { var constraintParts = constraint.Split(' '); var instruction = constraintParts[0]; if (instruction.Equals("terms")) { for (int j = 1; j < constraintParts.Count(); j++) { var instructArg = constraintParts[j]; var newTerm = new Term(instructArg, true) as ITerm; // if there already is a term at this index, need to reference with variable if (terms.Count > j - 1) { var existingTerm = terms[j - 1]; newTerm.Variable = existingTerm.Variable; newTerm.Type = existingTerm.Type; terms[j - 1] = newTerm; } else { terms.Add(newTerm); } } //foreach(var instructArg in constraintParts.Skip(1)) //{ // terms.Add(new Term(instructArg, true) as ITerm); //} } if (instruction.Equals("term")) { var argPos = Int32.Parse(constraintParts[1]); if (argPos >= terms.Count - 1) { // if the arg position is not the next item in term list, then while (argPos != terms.Count) { // add placeholder terms terms.Add(new Term(terms.Count.ToString()) as ITerm); } terms.Add(new Term(constraintParts[2], true) as ITerm); } else if (argPos < terms.Count) { terms.Insert(argPos, new Term(constraintParts[2], true) as ITerm); } } if (instruction.Equals("precond") || instruction.Equals("has-precond") || instruction.Equals("precondition")) { var pred = ProcessPredicateString(constraintParts); preconditions.Add(pred); } if (instruction.Equals("effect") || instruction.Equals("has-effect")) { var pred = ProcessPredicateString(constraintParts); effects.Add(pred); } if (instruction.Equals("schema")) { // then this operator gets a particular name schemaName = constraintParts[1]; } if (instruction.Equals("not")) { if (constraintParts[1].Equals("schema")) { // propagate to schema schema.AddConstraint(constraint); } // TODO: assemble list of negative constraints and propagate to filtering } } var root = new Operator(new Predicate(schemaName, terms, true), preconditions, effects); ps = new PlanStep(root as IOperator); return(ps); }
public void Assemble() { SubSteps = new List <IPlanStep>(); DSubSteps = new List <CamPlanStep>(); // Collect step-typed variables fabClipStepMap = new Dictionary <ClipSchema <FabulaAsset>, PlanStep>(); discClipStepMap = new Dictionary <ClipSchema <DiscourseAsset>, CamPlanStep>(); // used to identify references of constraints fabVarStepMap = new Dictionary <string, PlanStep>(); discVarStepMap = new Dictionary <string, CamPlanStep>(); // if there's a gap, then it's a "before" relation orderings = new List <Tuple <IPlanStep, IPlanStep> >(); dorderings = new List <Tuple <CamPlanStep, CamPlanStep> >(); // if the clip is contiguous with the last one, then it's "cntg" cntgs = new List <Tuple <IPlanStep, IPlanStep> >(); dcntgs = new List <Tuple <CamPlanStep, CamPlanStep> >(); // keep a map of literals based on names so that one can assign precondition and effects; specified in global constraints, only used for causal links locally var literalMap = new Dictionary <string, IPredicate>(); // collect links specified in constraints links = new List <CausalLink <IPlanStep> >(); dlinks = new List <CausalLink <CamPlanStep> >(); // constraints that are stored between shots or between actions, each a tuple stepConstraints = new List <Tuple <string, Tuple <PlanStep, PlanStep> > >(); dstepConstraints = new List <Tuple <string, Tuple <CamPlanStep, CamPlanStep> > >(); // keeping track of last clip to determine ordering and cntg relations with last. // TODO: support multiple fabula timelines ClipSchema <FabulaAsset> last = fabulaClips[0]; var visitedVariables = new List <string>(); for (int i = 0; i < fabulaClips.Count; i++) { var fabClip = fabulaClips[i]; // read the step variable from the asset var planStep = ReadStepVariable(fabClip); // Add terms to decomp terms foreach (var term in planStep.Terms) { if (visitedVariables.Contains(term.Variable)) { term.Variable = term.Variable + planStep.GetHashCode().ToString(); } visitedVariables.Add(term.Variable); Terms.Add(term); } SubSteps.Add(planStep); // now check against the last item in there for timing. if (i > 0) { var lastItemEndTime = last.start + last.duration; if (fabClip.start - lastItemEndTime < 0.3) { // these are cntg var newCntg = new Tuple <IPlanStep, IPlanStep>(fabClipStepMap[last], planStep); cntgs.Add(newCntg); } else if (fabClip.start - lastItemEndTime >= 0.3) { // what's the maximum amount of space that can go into here? This is calculated by looking at specific action. var ordering = new Tuple <IPlanStep, IPlanStep>(fabClipStepMap[last], planStep); orderings.Add(ordering); } } last = fabClip; fabClipStepMap.Add(fabClip, planStep); fabVarStepMap.Add(fabClip.display, planStep); } // ... including camera-step variables ClipSchema <DiscourseAsset> dlast = discourseClips[0]; for (int i = 0; i < discourseClips.Count; i++) //foreach (var discClip in discourseClips) { var discClip = discourseClips[i]; var camstep = ReadCamStepVariable(discClip); DSubSteps.Add(camstep); /// Thus, the camera plan shot cannot itself hold a fixed duration as a term as input (though a maximum default would be practical). /// Precisely what is needed are those details related to global constraints... /// The benefit of breaking them up is that it's clear that a new camera shot can be inserted between them with a new action segment. discClipStepMap[discClip] = camstep; discVarStepMap[discClip.display] = camstep; if (i > 0) { var lastItemEndTime = dlast.start + dlast.duration; if (discClip.start - lastItemEndTime < 0.3) { // these are cntg var newCntg = new Tuple <CamPlanStep, CamPlanStep>(discClipStepMap[dlast], camstep); dcntgs.Add(newCntg); } else if (discClip.start - lastItemEndTime >= 0.3) { // what's the maximum amount of space that can go into here? This is calculated by looking at specific action. var ordering = new Tuple <CamPlanStep, CamPlanStep>(discClipStepMap[dlast], camstep); dorderings.Add(ordering); } } dlast = discClip; } // iterate through global constraints foreach (var constraintClipSchema in globalConstraints) { var constraints = constraintClipSchema.asset.Constraints; foreach (var constraint in constraints) { var constraintParts = constraint.Split(' '); if (constraintParts[0].Equals("=")) { // this could mean that 2 variables are equal, or that a new constant is to be declared that has the name and terms } if (constraintParts[0].Equals("hangle")) { // hangle shot1 (between value1 value2) [[[[ this should be on single shot]]]] // hangle shot1 (reverse shot2) if (constraintParts[2].Equals("reverse")) { // do something } // hangle shot1 (differ 90 shot2) if (constraintParts[2].Equals("differ")) { // do something } } if (constraintParts[0].Equals("orient")) { // orient action1 reverse action2 if (constraintParts[2].Equals("reverse")) { var planstepTuple = new Tuple <PlanStep, PlanStep>(fabVarStepMap[constraintParts[1]], fabVarStepMap[constraintParts[3]]); var stepConstraintTuple = new Tuple <string, Tuple <PlanStep, PlanStep> >("orient reverse", planstepTuple); stepConstraints.Add(stepConstraintTuple); } // orient action1 = action2 if (constraintParts[2].Equals("=")) { var planstepTuple = new Tuple <PlanStep, PlanStep>(fabVarStepMap[constraintParts[1]], fabVarStepMap[constraintParts[3]]); var stepConstraintTuple = new Tuple <string, Tuple <PlanStep, PlanStep> >("orient =", planstepTuple); stepConstraints.Add(stepConstraintTuple); } } // user to define a literal as variable -- essentially only used for causal link dependencies if (constraintParts[0].Equals("literal")) { // literal literalAsVariableName predicatename, term_0, term_1...., term_k // indicates declaration of literal var predicate = ProcessPredicateString(constraintParts.Skip(1).ToArray()); } // if constraints[0] is some kind of ordering constraint that's different from those if (constraintParts[0].Equals("linked") || constraintParts[0].Equals("link") || constraintParts[0].Equals("linked-by")) { int sourceNum = 1; var dependency = new Predicate() as IPredicate; if (constraintParts.Count() == 4) { // linked dependencyCondition sourceStepname sinkStepName // 0 1 2 3 dependency = literalMap[constraintParts[1]]; sourceNum += 1; } // find the planstep based on the step-variable name var source = fabVarStepMap[constraintParts[sourceNum]]; var sink = fabVarStepMap[constraintParts[sourceNum + 1]]; var cl = new CausalLink <IPlanStep>(dependency, source, sink); links.Add(cl); } } } /// next we have to filter the schemas based on actual candidates. for steps that's groundActions, and for cams that's gameObjects with the camAttributeStruct /// then, we filter based on global constraints }