void Dialog_Waypoint::set_value_desc(synfigapp::ValueDesc value_desc) { value_desc_=value_desc; if(value_desc.get_value_node() && value_desc.get_value_node()->get_parent_canvas()) waypointwidget->set_canvas(value_desc.get_value_node()->get_parent_canvas()); }
static bool is_not_supported(const synfigapp::ValueDesc& x) { return x.get_value_type() == type_string || x.get_value_type() == type_canvas || x.get_value_type() == type_gradient || x.get_value_type() == type_list || x.get_value_type() == type_segment; }
int Action::KeyframeSet::scale_activepoints(const synfigapp::ValueDesc& value_desc,const synfig::Time& old_begin,const synfig::Time& old_end,const synfig::Time& new_begin,const synfig::Time& new_end) { ValueNode_DynamicList::Handle value_node(ValueNode_DynamicList::Handle::cast_static(value_desc.get_parent_value_node())); ValueNode_DynamicList::ListEntry& list_entry(value_node->list[value_desc.get_index()]); std::vector<Activepoint*> selected; std::vector<Activepoint*>::iterator iter; if(list_entry.find(old_begin,old_end,selected)) { // check to make sure this operation is OK for(iter=selected.begin();iter!=selected.end();++iter) { try { Time new_time(old_2_new((*iter)->get_time())); if(new_time>=old_begin && new_time<old_end) continue; list_entry.find(new_time); // If we found a activepoint already at that time, then // we need to abort //throw Exception::BadTime(_("Activepoint Conflict")); } catch(Exception::NotFound&) { } } int ret(0); while(!selected.empty()) { if(selected.back()->get_time()!=old_2_new(selected.back()->get_time())) { Action::Handle action(Action::create("ActivepointSet")); action->set_param("canvas",get_canvas()); action->set_param("canvas_interface",get_canvas_interface()); action->set_param("value_desc",value_desc); Activepoint activepoint(*selected.back()); activepoint.set_time(old_2_new(selected.back()->get_time())); action->set_param("activepoint",activepoint); assert(action->is_ready()); if(!action->is_ready()) throw Error(Error::TYPE_NOTREADY); add_action_front(action); ret++; } selected.pop_back(); } return ret; } return 0; }
void Action::KeyframeDuplicate::process_value_desc(const synfigapp::ValueDesc& value_desc) { const synfig::Time old_time=keyframe.get_time(); const synfig::Time new_time=new_keyframe.get_time(); if(value_desc.is_value_node()) { ValueNode::Handle value_node(value_desc.get_value_node()); // If we are a dynamic list, then we need to update the ActivePoints if(ValueNode_DynamicList::Handle::cast_dynamic(value_node)) { ValueNode_DynamicList::Handle value_node_dynamic(ValueNode_DynamicList::Handle::cast_dynamic(value_node)); int i; for(i=0;i<value_node_dynamic->link_count();i++) { synfigapp::ValueDesc value_desc(value_node_dynamic,i); Activepoint activepoint(value_node_dynamic->list[i].new_activepoint_at_time(old_time)); activepoint.set_time(new_time); Action::Handle action(ActivepointSetSmart::create()); action->set_param("canvas",get_canvas()); action->set_param("canvas_interface",get_canvas_interface()); action->set_param("value_desc",value_desc); action->set_param("activepoint",activepoint); assert(action->is_ready()); if(!action->is_ready()) throw Error(Error::TYPE_NOTREADY); add_action_front(action); } } else if(ValueNode_Animated::Handle::cast_dynamic(value_node)) { ValueNode_Animated::Handle value_node_animated(ValueNode_Animated::Handle::cast_dynamic(value_node)); Waypoint waypoint(value_node_animated->new_waypoint_at_time(old_time)); waypoint.set_time(new_time); Action::Handle action(WaypointSetSmart::create()); action->set_param("canvas",get_canvas()); action->set_param("canvas_interface",get_canvas_interface()); action->set_param("value_node",ValueNode::Handle(value_node_animated)); action->set_param("waypoint",waypoint); assert(action->is_ready()); if(!action->is_ready()) throw Error(Error::TYPE_NOTREADY); add_action_front(action); } } }
//kind of a hack... pointer is ugly void get_change_times_from_vdesc(const synfigapp::ValueDesc &v, std::set<synfig::Time> ×) { if (v.is_value_node()) { if ( v.get_value_type() == type_string || v.get_value_type() == type_bool || v.get_value_type() == type_canvas ) { std::map<Time, ValueBase> x; v.get_value_node()->get_values(x); for(std::map<Time, ValueBase>::const_iterator i = x.begin(); i != x.end(); ++i) times.insert(i->first); } } }
int Action::KeyframeSet::scale_waypoints(const synfigapp::ValueDesc& value_desc,const synfig::Time& old_begin,const synfig::Time& old_end,const synfig::Time& new_begin,const synfig::Time& new_end) { ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_static(value_desc.get_value_node())); std::vector<Waypoint*> selected; std::vector<Waypoint*>::iterator iter; if(value_node->find(old_begin,old_end,selected)) { // check to make sure this operation is OK for(iter=selected.begin();iter!=selected.end();++iter) { try { Time new_time(old_2_new((*iter)->get_time())); if(new_time>=old_begin && new_time<old_end) continue; value_node->find(new_time); // If we found a waypoint point already at that time, then // we need to abort //synfig::info(_("old_begin: %s, old_end: %s"),old_begin.get_string().c_str(),old_end.get_string().c_str()); //synfig::info(_("new_begin: %s, new_end: %s"),new_begin.get_string().c_str(),new_end.get_string().c_str()); //throw Exception::BadTime(strprintf(_("Waypoint Conflict, old: %s, new: %s"),(*iter)->get_time().get_string().c_str(),new_time.get_string().c_str())); } catch(Exception::NotFound&) { } } int ret(0); while(!selected.empty()) { if(selected.back()->get_time()!=old_2_new(selected.back()->get_time())) { Action::Handle action(Action::create("WaypointSet")); action->set_param("canvas",get_canvas()); action->set_param("canvas_interface",get_canvas_interface()); action->set_param("value_node",ValueNode::Handle::cast_static(value_node)); Waypoint waypoint(*selected.back()); waypoint.set_time(old_2_new(selected.back()->get_time())); action->set_param("waypoint",waypoint); assert(action->is_ready()); if(!action->is_ready()) throw Error(Error::TYPE_NOTREADY); add_action_front(action); ret++; } selected.pop_back(); } return ret; } return 0; }
void Action::KeyframeWaypointSet::process_value_desc(const synfigapp::ValueDesc& value_desc) { if(value_desc.is_value_node()) { ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node())); if(value_node) { Action::Handle action(WaypointSetSmart::create()); action->set_param("canvas",get_canvas()); action->set_param("canvas_interface",get_canvas_interface()); action->set_param("value_node",ValueNode::Handle(value_node)); Waypoint waypoint; try { waypoint=*value_node->find(keyframe.get_time()); } catch(...) { waypoint.set_time(keyframe.get_time()); waypoint.set_value((*value_node)(keyframe.get_time())); } keyframe.apply_model(waypoint_model); //*get_canvas()->keyframe_list().find(keyframe)=keyframe; KeyframeList::iterator iter; if (get_canvas()->keyframe_list().find(keyframe, iter)) *iter = keyframe; waypoint.apply_model(waypoint_model); action->set_param("waypoint",waypoint); assert(action->is_ready()); if(!action->is_ready()) throw Error(Error::TYPE_NOTREADY); add_action_front(action); } } }
const synfig::Time get_time_offset_from_vdesc(const synfigapp::ValueDesc &v) { #ifdef ADJUST_WAYPOINTS_FOR_TIME_OFFSET if(getenv("SYNFIG_SHOW_CANVAS_PARAM_WAYPOINTS") || v.get_value_type() != synfig::type_canvas) return synfig::Time::zero(); synfig::Canvas::Handle canvasparam = v.get_value().get(Canvas::Handle()); if(!canvasparam) return synfig::Time::zero(); if (!v.parent_is_layer()) return synfig::Time::zero(); synfig::Layer::Handle layer = v.get_layer(); if (etl::handle<Layer_PasteCanvas>::cast_dynamic(layer)) return synfig::Time::zero(); return layer->get_param("time_offset").get(Time()); #else // ADJUST_WAYPOINTS_FOR_TIME_OFFSET return synfig::Time::zero(); #endif }
CurveStruct(const synfigapp::ValueDesc& x): value_desc(x) { Type &type(value_desc.get_value_type()); if (type == type_real) { channels.push_back(Channel()); channels.back().name="real"; channels.back().color=Gdk::Color("#007f7f"); } else if (type == type_time) { channels.push_back(Channel()); channels.back().name="time"; channels.back().color=Gdk::Color("#7f7f00"); } else if (type == type_integer) { channels.push_back(Channel()); channels.back().name="int"; channels.back().color=Gdk::Color("#7f0000"); } else if (type == type_bool) { channels.push_back(Channel()); channels.back().name="bool"; channels.back().color=Gdk::Color("#ff7f00"); } else if (type == type_angle) { channels.push_back(Channel()); channels.back().name="theta"; channels.back().color=Gdk::Color("#004f4f"); } else if (type == type_color) { channels.push_back(Channel()); channels.back().name="red"; channels.back().color=Gdk::Color("#7f0000"); channels.push_back(Channel()); channels.back().name="green"; channels.back().color=Gdk::Color("#007f00"); channels.push_back(Channel()); channels.back().name="blue"; channels.back().color=Gdk::Color("#00007f"); channels.push_back(Channel()); channels.back().name="alpha"; channels.back().color=Gdk::Color("#000000"); } else if (type == type_vector) { channels.push_back(Channel()); channels.back().name="x"; channels.back().color=Gdk::Color("#7f007f"); channels.push_back(Channel()); channels.back().name="y"; channels.back().color=Gdk::Color("#007f7f"); } else if (type == type_bline_point) { channels.push_back(Channel()); channels.back().name="v.x"; channels.back().color=Gdk::Color("#ff7f00"); channels.push_back(Channel()); channels.back().name="v.y"; channels.back().color=Gdk::Color("#7f3f00"); channels.push_back(Channel()); channels.back().name="width"; channels.back().color=Gdk::Color("#000000"); channels.push_back(Channel()); channels.back().name="origin"; channels.back().color=Gdk::Color("#ffffff"); channels.push_back(Channel()); channels.back().name="tsplit"; channels.back().color=Gdk::Color("#ff00ff"); channels.push_back(Channel()); channels.back().name="t1.x"; channels.back().color=Gdk::Color("#ff0000"); channels.push_back(Channel()); channels.back().name="t1.y"; channels.back().color=Gdk::Color("#7f0000"); channels.push_back(Channel()); channels.back().name="t2.x"; channels.back().color=Gdk::Color("#ffff00"); channels.push_back(Channel()); channels.back().name="t2.y"; channels.back().color=Gdk::Color("#7f7f00"); channels.push_back(Channel()); channels.back().name="rsplit"; channels.back().color=Gdk::Color("#ff00ff"); channels.push_back(Channel()); channels.back().name="asplit"; channels.back().color=Gdk::Color("#ff00ff"); } else if (type == type_width_point) { channels.push_back(Channel()); channels.back().name="position"; channels.back().color=Gdk::Color("#ff0000"); channels.push_back(Channel()); channels.back().name="width"; channels.back().color=Gdk::Color("#00ff00"); } else if (type == type_dash_item) { channels.push_back(Channel()); channels.back().name="offset"; channels.back().color=Gdk::Color("#ff0000"); channels.push_back(Channel()); channels.back().name="length"; channels.back().color=Gdk::Color("#00ff00"); } else { throw synfig::Exception::BadType("Bad type for curves"); } }
synfig::Real get_value(int chan, synfig::Real time, synfig::Real tolerance) { std::map<synfig::Real,synfig::Real>::iterator iter; // First check to see if we have a value // that is "close enough" to the time // we are looking for iter=channels[chan].values.lower_bound(time); if(iter!=channels[chan].values.end() && iter->first-time<=tolerance) return -iter->second; // Since that didn't work, we now need // to go ahead and figure out what the // actual value is at that time. ValueBase value(value_desc.get_value(time)); Type &type(value.get_type()); if (type == type_real) channels[0].values[time]=value.get(Real()); else if (type == type_time) channels[0].values[time]=value.get(Time()); else if (type == type_integer) channels[0].values[time]=value.get(int()); else if (type == type_bool) channels[0].values[time]=value.get(bool()); else if (type == type_angle) channels[0].values[time]=Angle::rad(value.get(Angle())).get(); else if (type == type_color) { channels[0].values[time]=value.get(Color()).get_r(); channels[1].values[time]=value.get(Color()).get_g(); channels[2].values[time]=value.get(Color()).get_b(); channels[3].values[time]=value.get(Color()).get_a(); } else if (type == type_vector) { channels[0].values[time]=value.get(Vector())[0]; channels[1].values[time]=value.get(Vector())[1]; } else if (type == type_bline_point) { channels[0].values[time]=value.get(BLinePoint()).get_vertex()[0]; channels[1].values[time]=value.get(BLinePoint()).get_vertex()[1]; channels[2].values[time]=value.get(BLinePoint()).get_width(); channels[3].values[time]=value.get(BLinePoint()).get_origin(); channels[4].values[time]=value.get(BLinePoint()).get_split_tangent_both(); channels[5].values[time]=value.get(BLinePoint()).get_tangent1()[0]; channels[6].values[time]=value.get(BLinePoint()).get_tangent1()[1]; channels[7].values[time]=value.get(BLinePoint()).get_tangent2()[0]; channels[8].values[time]=value.get(BLinePoint()).get_tangent2()[1]; channels[9].values[time]=value.get(BLinePoint()).get_split_tangent_radius(); channels[10].values[time]=value.get(BLinePoint()).get_split_tangent_angle(); } else if (type == type_width_point) { channels[0].values[time]=value.get(WidthPoint()).get_position(); channels[1].values[time]=value.get(WidthPoint()).get_width(); } else { return 0; } return -channels[chan].values[time]; }
void Action::KeyframeRemove::process_value_desc(const synfigapp::ValueDesc& value_desc) { const synfig::Time time(keyframe.get_time()); if(value_desc.is_value_node()) { ValueNode::Handle value_node(value_desc.get_value_node()); // If we are a dynamic list, then we need to update the ActivePoints if(ValueNode_DynamicList::Handle::cast_dynamic(value_node)) { ValueNode_DynamicList::Handle value_node_dynamic(ValueNode_DynamicList::Handle::cast_dynamic(value_node)); int i; for(i=0;i<value_node_dynamic->link_count();i++) try { Activepoint activepoint; activepoint=*value_node_dynamic->list[i].find(time); synfigapp::ValueDesc value_desc(value_node_dynamic,i); Action::Handle action(ActivepointRemove::create()); action->set_param("canvas",get_canvas()); action->set_param("canvas_interface",get_canvas_interface()); action->set_param("value_desc",value_desc); action->set_param("activepoint",activepoint); assert(action->is_ready()); if(!action->is_ready()) throw Error(Error::TYPE_NOTREADY); add_action_front(action); } catch(...) { } } else if(ValueNode_Animated::Handle::cast_dynamic(value_node)) try { ValueNode_Animated::Handle value_node_animated(ValueNode_Animated::Handle::cast_dynamic(value_node)); Waypoint waypoint; waypoint=*value_node_animated->find(time); assert(waypoint.get_time()==time); Action::Handle action(WaypointRemove::create()); action->set_param("canvas",get_canvas()); action->set_param("canvas_interface",get_canvas_interface()); action->set_param("value_node",ValueNode::Handle(value_node_animated)); action->set_param("waypoint",waypoint); assert(action->is_ready()); if(!action->is_ready()) throw Error(Error::TYPE_NOTREADY); add_action_front(action); } catch(...) { } } }
void Action::KeyframeSet::process_value_desc(const synfigapp::ValueDesc& value_desc) { if(value_desc.is_value_node()) { ValueNode::Handle value_node(value_desc.get_value_node()); //if(guid_set.count(value_node->get_guid())) // return; //guid_set.insert(value_node->get_guid()); // If we are a dynamic list, then we need to update the ActivePoints if(ValueNode_DynamicList::Handle::cast_dynamic(value_node)) { ValueNode_DynamicList::Handle value_node_dynamic(ValueNode_DynamicList::Handle::cast_dynamic(value_node)); int i; for(i=0;i<value_node_dynamic->link_count();i++) { synfigapp::ValueDesc value_desc(value_node_dynamic,i); if(new_time>keyframe_prev && new_time<keyframe_next) { // In this circumstance, we need to adjust any // activepoints between the previous and next // keyframes scale_activepoints(value_desc,keyframe_prev,old_time,keyframe_prev,new_time); scale_activepoints(value_desc,old_time,keyframe_next,new_time,keyframe_next); } //else { Action::Handle action(ActivepointSetSmart::create()); action->set_param("canvas",get_canvas()); action->set_param("canvas_interface",get_canvas_interface()); action->set_param("value_desc",value_desc); Activepoint activepoint; try { activepoint=*value_node_dynamic->list[i].find(old_time); activepoint.set_time(new_time); } catch(...) { activepoint.set_time(new_time); activepoint.set_state(value_node_dynamic->list[i].status_at_time(old_time)); activepoint.set_priority(0); } action->set_param("activepoint",activepoint); assert(action->is_ready()); if(!action->is_ready()) throw Error(Error::TYPE_NOTREADY); add_action_front(action); } } } else if(ValueNode_Animated::Handle::cast_dynamic(value_node)) { if(new_time>keyframe_prev && new_time<keyframe_next) { // In this circumstance, we need to adjust any // waypoints between the previous and next // keyframes scale_waypoints(value_desc,keyframe_prev,old_time,keyframe_prev,new_time); scale_waypoints(value_desc,old_time,keyframe_next,new_time,keyframe_next); } //else { ValueNode_Animated::Handle value_node_animated(ValueNode_Animated::Handle::cast_dynamic(value_node)); Action::Handle action(WaypointSetSmart::create()); action->set_param("canvas",get_canvas()); action->set_param("canvas_interface",get_canvas_interface()); action->set_param("value_node",ValueNode::Handle(value_node_animated)); Waypoint waypoint; try { waypoint=*value_node_animated->find(old_time); waypoint.set_time(new_time); } catch(...) { waypoint.set_time(new_time); waypoint.set_value((*value_node_animated)(old_time)); waypoint.set_before(synfigapp::Main::get_interpolation()); waypoint.set_after(synfigapp::Main::get_interpolation()); } action->set_param("waypoint",waypoint); assert(action->is_ready()); if(!action->is_ready()) throw Error(Error::TYPE_NOTREADY); add_action_front(action); } } } }
void CanvasTreeStore::set_row(Gtk::TreeRow row,synfigapp::ValueDesc value_desc, bool do_children) { Gtk::TreeModel::Children children = row.children(); while(!children.empty() && erase(children.begin())) ; row[model.value_desc]=value_desc; try { //row[model.icon] = get_tree_pixbuf(value_desc.get_value_type()); if(value_desc.is_value_node()) { ValueNode::Handle value_node=value_desc.get_value_node(); // todo: if the parent is animated and expanded, and we drag the time slider so that it changes, // it's not updated. it still shows the previous bone valuenode. // maybe replace the ValueNode_Const or ValueNode_Animated with the contained ValueNode_Bone value_node = expandable_bone_parent(value_node); assert(value_node); row[model.value_node] = value_node; //row[model.is_canvas] = false; //row[model.is_value_node] = true; //row[model.is_editable] = synfigapp::is_editable(value_node); //row[model.id]=value_node->get_id(); // Set the canvas if(value_desc.parent_is_canvas()) row[model.canvas]=value_desc.get_canvas(); else row[model.canvas]=canvas_interface()->get_canvas(); LinkableValueNode::Handle linkable; // printf("%s:%d value_node = %s\n", __FILE__, __LINE__, value_node->get_description().c_str()); linkable=LinkableValueNode::Handle::cast_dynamic(value_node); // printf("linkable: %d; do_children: %d\n", bool(linkable), bool(do_children)); if(linkable && do_children) { row[model.link_count] = linkable->link_count(); LinkableValueNode::Vocab vocab(linkable->get_children_vocab()); LinkableValueNode::Vocab::iterator iter(vocab.begin()); for(int i=0;i<linkable->link_count();i++, iter++) { if(iter->get_hidden()) continue; Gtk::TreeRow child_row=*(append(row.children())); child_row[model.link_id] = i; child_row[model.canvas] = static_cast<Canvas::Handle>(row[model.canvas]); child_row[model.name] = linkable->link_local_name(i); child_row[model.tooltip] = iter->get_description(); child_row[model.child_param_desc] = *iter; set_row(child_row,synfigapp::ValueDesc(linkable,i)); } } return; } else { //row[model.is_value_node] = false; //row[model.is_editable] = true; //row[model.label] = Glib::ustring(row[model.name]); return; } } catch(synfig::Exception::IDNotFound x) { synfig::error(__FILE__":%d: IDNotFound thrown",__LINE__); erase(row); return; } // We should never get to this point assert(0); }
void synfigapp::recurse_valuedesc(synfigapp::ValueDesc h, const std::set<Time> &tlist, timepoints_ref &vals, synfig::Time time_offset, synfig::Real time_dilation) { //special cases for Animated, DynamicList, and Linkable //synfig::info("ValueBasenode... %p, %s", h.get_value_node().get(),typeid(*h.get_value_node()).name()); //animated case { synfig::ValueNode_Animated::Handle p = synfig::ValueNode_Animated::Handle::cast_dynamic(h.get_value_node()); if(p) { //loop through and determine which waypoint we will need to reference const synfig::WaypointList &w = p->waypoint_list(); synfig::WaypointList::const_iterator i = w.begin(), end = w.end(); std::set<Time>::const_iterator j = tlist.begin(), jend = tlist.end(); for(; i != end && j != jend;) { //synfig::info("tpair t(%.3f) = %.3f", (float)*j, (float)(i->get_time())); if((*j*time_dilation+time_offset).is_equal(i->get_time())) { vals.insert(p,*i,time_dilation); ++i,++j; }else if(*i < *j*time_dilation+time_offset) { ++i; }else ++j; } return; } } //parent dynamiclist case - just for active points for that object... if(h.parent_is_value_node()) { synfig::ValueNode_DynamicList::Handle p = synfig::ValueNode_DynamicList::Handle::cast_dynamic(h.get_parent_value_node()); if(p) { int index = h.get_index(); //check all the active points in each list... const synfig::ActivepointList &a = p->list[index].timing_info; //synfig::info("Our parent = dynamic list, searching in %d activepts",a.size()); std::set<Time>::const_iterator i = tlist.begin(), end = tlist.end(); synfig::ActivepointList::const_iterator j = a.begin(), jend = a.end(); for(; j != jend && i != end;) { double it = *i*time_dilation+time_offset; double jt = j->get_time(); double diff = (double)(it - jt); //synfig::info("\ttpair match(%.4lg) - %.4lg (diff = %lg",it,jt,diff); // if(abs(diff) < (double)Time::epsilon()) { //synfig::info("\tActivepoint to add being referenced (%x,%s,%.4lg)", // (int)j->get_uid(),j->state?"true":"false", (double)j->time); vals.insert(ValueDesc(p,index),*j,time_dilation); ++i,++j; }else if(it < jt) { ++i; //synfig::info("\tIncrementing time"); } else { ++j; //synfig::info("\tIncrementing actpt"); } } } } //dynamiclist case - we must still make sure that we read from the list entries the time values // because just the linked valuenodes will not do that { synfig::ValueNode_DynamicList::Handle p = synfig::ValueNode_DynamicList::Handle::cast_dynamic(h.get_value_node()); if(p) { //synfig::info("Process dynamic list valuenode"); int index = 0; std::vector<synfig::ValueNode_DynamicList::ListEntry>::const_iterator i = p->list.begin(), end = p->list.end(); for(; i != end; ++i, ++index) { const Node::time_set &tset = i->get_times(); if(check_intersect(tset.begin(),tset.end(),tlist.begin(),tlist.end(),time_offset,time_dilation)) { recurse_valuedesc(ValueDesc(p,index),tlist,vals,time_offset,time_dilation); } } return; } } //the linkable case... { etl::handle<synfig::LinkableValueNode> p = etl::handle<synfig::LinkableValueNode>::cast_dynamic(h.get_value_node()); if(p) { //synfig::info("Process Linkable ValueBasenode"); int i = 0, size = p->link_count(); for(; i < size; ++i) { ValueNode::Handle v = p->get_link(i); const Node::time_set &tset = v->get_times(); if(check_intersect(tset.begin(),tset.end(),tlist.begin(),tlist.end(),time_offset,time_dilation)) { recurse_valuedesc(ValueDesc(p,i),tlist,vals,time_offset,time_dilation); } } } } }