Support toggling key frames.

This commit is contained in:
Leonardo Zide
2024-01-21 12:53:18 -08:00
parent 2f40cf5bc7
commit a42d86394c
16 changed files with 333 additions and 17 deletions
+62
View File
@@ -628,6 +628,68 @@ bool lcCamera::HasKeyFrame(lcObjectPropertyId PropertyId, lcStep Time) const
return false;
}
bool lcCamera::SetKeyFrame(lcObjectPropertyId PropertyId, lcStep Time, bool KeyFrame)
{
switch (PropertyId)
{
case lcObjectPropertyId::PieceId:
case lcObjectPropertyId::PieceColor:
case lcObjectPropertyId::PieceStepShow:
case lcObjectPropertyId::PieceStepHide:
case lcObjectPropertyId::CameraName:
case lcObjectPropertyId::CameraType:
case lcObjectPropertyId::CameraFOV:
case lcObjectPropertyId::CameraNear:
case lcObjectPropertyId::CameraFar:
return false;
case lcObjectPropertyId::CameraPositionX:
case lcObjectPropertyId::CameraPositionY:
case lcObjectPropertyId::CameraPositionZ:
return mPosition.SetKeyFrame(Time, KeyFrame);
case lcObjectPropertyId::CameraTargetX:
case lcObjectPropertyId::CameraTargetY:
case lcObjectPropertyId::CameraTargetZ:
return mTargetPosition.SetKeyFrame(Time, KeyFrame);
case lcObjectPropertyId::CameraUpX:
case lcObjectPropertyId::CameraUpY:
case lcObjectPropertyId::CameraUpZ:
return mUpVector.SetKeyFrame(Time, KeyFrame);
case lcObjectPropertyId::LightName:
case lcObjectPropertyId::LightType:
case lcObjectPropertyId::LightColor:
case lcObjectPropertyId::LightPower:
case lcObjectPropertyId::LightCastShadow:
case lcObjectPropertyId::LightAttenuationDistance:
case lcObjectPropertyId::LightAttenuationPower:
case lcObjectPropertyId::LightPointSize:
case lcObjectPropertyId::LightSpotSize:
case lcObjectPropertyId::LightDirectionalSize:
case lcObjectPropertyId::LightAreaSize:
case lcObjectPropertyId::LightAreaSizeX:
case lcObjectPropertyId::LightAreaSizeY:
case lcObjectPropertyId::LightSpotConeAngle:
case lcObjectPropertyId::LightSpotPenumbraAngle:
case lcObjectPropertyId::LightSpotTightness:
case lcObjectPropertyId::LightAreaShape:
case lcObjectPropertyId::LightAreaGridX:
case lcObjectPropertyId::LightAreaGridY:
case lcObjectPropertyId::ObjectPositionX:
case lcObjectPropertyId::ObjectPositionY:
case lcObjectPropertyId::ObjectPositionZ:
case lcObjectPropertyId::ObjectRotationX:
case lcObjectPropertyId::ObjectRotationY:
case lcObjectPropertyId::ObjectRotationZ:
case lcObjectPropertyId::Count:
return false;
}
return false;
}
void lcCamera::RemoveKeyFrames()
{
mPosition.RemoveAllKeys();
+2 -1
View File
@@ -272,6 +272,7 @@ public:
void BoxTest(lcObjectBoxTest& ObjectBoxTest) const override;
void DrawInterface(lcContext* Context, const lcScene& Scene) const override;
bool HasKeyFrame(lcObjectPropertyId PropertyId, lcStep Time) const override;
bool SetKeyFrame(lcObjectPropertyId PropertyId, lcStep Time, bool KeyFrame) override;
void RemoveKeyFrames() override;
void InsertTime(lcStep Start, lcStep Time);
@@ -280,7 +281,7 @@ public:
static bool FileLoad(lcFile& file);
void CompareBoundingBox(lcVector3& Min, lcVector3& Max);
void UpdatePosition(lcStep Step);
void UpdatePosition(lcStep Step) override;
void CopyPosition(const lcCamera* Camera);
void CopySettings(const lcCamera* Camera);
+7
View File
@@ -21,6 +21,13 @@ public:
*this = Array;
}
lcArray(std::initializer_list<T> Init)
: lcArray((int)Init.size())
{
for (const T& Element : Init)
Add(Element);
}
~lcArray()
{
delete[] mData;
+7 -4
View File
@@ -2,7 +2,7 @@
#include "lc_keyframewidget.h"
lcKeyFrameWidget::lcKeyFrameWidget(QWidget* Parent)
: QAbstractButton(Parent)
: QCheckBox(Parent)
{
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
setCheckable(true);
@@ -17,11 +17,14 @@ void lcKeyFrameWidget::paintEvent(QPaintEvent* PaintEvent)
QRect Rect = rect();
Painter.fillRect(Rect, palette().brush(QPalette::Window));
Qt::CheckState State = checkState();
Painter.setPen(hasFocus() ? palette().color(QPalette::Highlight) : palette().color(QPalette::Shadow));
Painter.setBrush(palette().color(QPalette::Text));
Rect = isChecked() ? QRect(1, 1, 13, 13) : QRect(4, 4, 7, 7);
QPoint Center = Rect.center();
if (State != Qt::PartiallyChecked)
Painter.setBrush(palette().color(QPalette::Text));
Rect = (State != Qt::Unchecked) ? QRect(1, 1, 13, 13) : QRect(4, 4, 7, 7);
const QPoint Center = Rect.center();
QPoint Points[4] =
{
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
class lcKeyFrameWidget : public QAbstractButton
class lcKeyFrameWidget : public QCheckBox
{
Q_OBJECT
+18
View File
@@ -2973,6 +2973,24 @@ void lcModel::TransformSelectedObjects(lcTransformType TransformType, const lcVe
}
}
void lcModel::SetObjectsKeyFrame(const lcArray<lcObject*>& Objects, lcObjectPropertyId PropertyId, bool KeyFrame)
{
bool Modified = false;
for (lcObject* Object : Objects)
{
Modified |= Object->SetKeyFrame(PropertyId, mCurrentStep, KeyFrame);
Object->UpdatePosition(mCurrentStep);
}
if (Modified)
{
SaveCheckpoint(tr("Changing Key Frame"));
gMainWindow->UpdateSelectedObjects(false);
UpdateAllViews();
}
}
void lcModel::SetSelectedPiecesColorIndex(int ColorIndex)
{
bool Modified = false;
+3
View File
@@ -4,6 +4,8 @@
#include "lc_commands.h"
#include "lc_array.h"
enum class lcObjectPropertyId;
#define LC_SEL_NO_PIECES 0x0001 // No pieces in model
#define LC_SEL_PIECE 0x0002 // At least 1 piece selected
#define LC_SEL_CAMERA 0x0004 // At least 1 camera selected
@@ -357,6 +359,7 @@ public:
void RotateSelectedObjects(const lcVector3& Angles, bool Relative, bool RotatePivotPoint, bool Update, bool Checkpoint);
void ScaleSelectedPieces(const float Scale, bool Update, bool Checkpoint);
void TransformSelectedObjects(lcTransformType TransformType, const lcVector3& Transform);
void SetObjectsKeyFrame(const lcArray<lcObject*>& Objects, lcObjectPropertyId PropertyId, bool KeyFrame);
void SetSelectedPiecesColorIndex(int ColorIndex);
void SetSelectedPiecesPieceInfo(PieceInfo* Info);
void SetSelectedPiecesStepShow(lcStep Step);
+41
View File
@@ -8,6 +8,7 @@
template void lcObjectProperty<T>::InsertTime(lcStep Start, lcStep Time); \
template void lcObjectProperty<T>::RemoveTime(lcStep Start, lcStep Time); \
template bool lcObjectProperty<T>::HasKeyFrame(lcStep Time) const; \
template bool lcObjectProperty<T>::SetKeyFrame(lcStep Time, bool KeyFrame); \
template void lcObjectProperty<T>::Save(QTextStream& Stream, const char* ObjectName, const char* VariableName, bool SaveEmpty) const; \
template bool lcObjectProperty<T>::Load(QTextStream& Stream, const QString& Token, const char* VariableName);
@@ -176,6 +177,46 @@ bool lcObjectProperty<T>::HasKeyFrame(lcStep Time) const
return false;
}
template<typename T>
bool lcObjectProperty<T>::SetKeyFrame(lcStep Time, bool KeyFrame)
{
if (KeyFrame)
{
typename std::vector<lcObjectPropertyKey<T>>::const_iterator KeyIt;
for (KeyIt = mKeys.begin(); KeyIt != mKeys.end(); KeyIt++)
{
if (KeyIt->Step == Time)
return false;
else if (KeyIt->Step > Time)
break;
}
mKeys.insert(KeyIt, lcObjectPropertyKey<T>{ Time, mValue });
return true;
}
else
{
for (typename std::vector<lcObjectPropertyKey<T>>::const_iterator KeyIt = mKeys.begin(); KeyIt != mKeys.end(); KeyIt++)
{
if (KeyIt->Step == Time)
{
if (mKeys.size() == 1)
mValue = KeyIt->Value;
mKeys.erase(KeyIt);
return true;
}
else if (KeyIt->Step > Time)
return false;
}
}
return false;
}
template<typename T>
void lcObjectProperty<T>::Save(QTextStream& Stream, const char* ObjectName, const char* VariableName, bool SaveEmpty) const
{
+1
View File
@@ -86,6 +86,7 @@ public:
void InsertTime(lcStep Start, lcStep Time);
void RemoveTime(lcStep Start, lcStep Time);
bool HasKeyFrame(lcStep Time) const;
bool SetKeyFrame(lcStep Time, bool KeyFrame);
void Save(QTextStream& Stream, const char* ObjectName, const char* VariableName, bool SaveEmpty) const;
bool Load(QTextStream& Stream, const QString& Token, const char* VariableName);
+40 -7
View File
@@ -101,7 +101,10 @@ void lcPropertiesWidget::KeyFrameChanged()
if (!Model)
return;
// todo: toggle keys in model
if (mFocusObject)
Model->SetObjectsKeyFrame({ mFocusObject }, PropertyId, Widget->isChecked());
else
Model->SetObjectsKeyFrame(mSelection, PropertyId, Widget->isChecked());
}
void lcPropertiesWidget::UpdateKeyFrameWidget(lcObjectPropertyId PropertyId)
@@ -113,7 +116,30 @@ void lcPropertiesWidget::UpdateKeyFrameWidget(lcObjectPropertyId PropertyId)
QSignalBlocker Blocker(Widget);
lcModel* Model = gMainWindow->GetActiveModel();
Widget->setChecked(mFocusObject && Model && mFocusObject->HasKeyFrame(PropertyId, Model->GetCurrentStep()));
if (Model)
{
const lcStep Step = Model->GetCurrentStep();
if (mFocusObject)
Widget->setChecked(mFocusObject->HasKeyFrame(PropertyId, Step));
else
{
int KeyFrameCount = 0, NonKeyFrameCount = 0;
for (const lcObject* Object : mSelection)
{
if (Object->HasKeyFrame(PropertyId, Step))
KeyFrameCount++;
else
NonKeyFrameCount++;
}
if (KeyFrameCount && NonKeyFrameCount)
Widget->setCheckState(Qt::PartiallyChecked);
else
Widget->setCheckState(KeyFrameCount != 0 ? Qt::Checked : Qt::Unchecked);
}
}
}
}
@@ -122,7 +148,7 @@ void lcPropertiesWidget::AddKeyFrameWidget(lcObjectPropertyId PropertyId)
lcKeyFrameWidget* Widget = new lcKeyFrameWidget(this);
Widget->setToolTip(tr("Toggle Key Frame"));
connect(Widget, &QCheckBox::toggled, this, &lcPropertiesWidget::KeyFrameChanged);
connect(Widget, &QCheckBox::stateChanged, this, &lcPropertiesWidget::KeyFrameChanged);
mLayout->addWidget(Widget, mLayoutRow, 3);
@@ -1029,6 +1055,7 @@ void lcPropertiesWidget::SetEmpty()
SetLayoutMode(LayoutMode::Empty);
mFocusObject = nullptr;
mSelection.RemoveAll();
}
void lcPropertiesWidget::SetPiece(const lcArray<lcObject*>& Selection, lcObject* Focus)
@@ -1036,6 +1063,7 @@ void lcPropertiesWidget::SetPiece(const lcArray<lcObject*>& Selection, lcObject*
SetLayoutMode(LayoutMode::Piece);
lcPiece* Piece = dynamic_cast<lcPiece*>(Focus);
mSelection = Selection;
mFocusObject = Piece;
lcVector3 Position;
@@ -1117,11 +1145,12 @@ void lcPropertiesWidget::SetPiece(const lcArray<lcObject*>& Selection, lcObject*
UpdateStepNumber(lcObjectPropertyId::PieceStepHide, StepHide ? StepHide : LC_STEP_MAX, StepShow + 1, LC_STEP_MAX);
}
void lcPropertiesWidget::SetCamera(lcObject* Focus)
void lcPropertiesWidget::SetCamera(const lcArray<lcObject*>& Selection, lcObject* Focus)
{
SetLayoutMode(LayoutMode::Camera);
lcCamera* Camera = dynamic_cast<lcCamera*>(Focus);
mSelection = Selection;
mFocusObject = Camera;
lcVector3 Position(0.0f, 0.0f, 0.0f);
@@ -1166,11 +1195,12 @@ void lcPropertiesWidget::SetCamera(lcObject* Focus)
UpdateFloat(lcObjectPropertyId::CameraUpZ, UpVector[2]);
}
void lcPropertiesWidget::SetLight(lcObject* Focus)
void lcPropertiesWidget::SetLight(const lcArray<lcObject*>& Selection, lcObject* Focus)
{
SetLayoutMode(LayoutMode::Light);
lcLight* Light = dynamic_cast<lcLight*>(Focus);
mSelection = Selection;
mFocusObject = Light;
QString Name;
@@ -1276,6 +1306,9 @@ void lcPropertiesWidget::SetLight(lcObject* Focus)
void lcPropertiesWidget::Update(const lcArray<lcObject*>& Selection, lcObject* Focus)
{
mFocusObject = nullptr;
mSelection.RemoveAll();
LayoutMode Mode = LayoutMode::Empty;
if (Focus)
@@ -1353,11 +1386,11 @@ void lcPropertiesWidget::Update(const lcArray<lcObject*>& Selection, lcObject* F
break;
case LayoutMode::Camera:
SetCamera(Focus);
SetCamera(Selection, Focus);
break;
case LayoutMode::Light:
SetLight(Focus);
SetLight(Selection, Focus);
break;
}
}
+3 -2
View File
@@ -108,8 +108,8 @@ protected:
void SetEmpty();
void SetPiece(const lcArray<lcObject*>& Selection, lcObject* Focus);
void SetCamera(lcObject* Focus);
void SetLight(lcObject* Focus);
void SetCamera(const lcArray<lcObject*>& Selection, lcObject* Focus);
void SetLight(const lcArray<lcObject*>& Selection, lcObject* Focus);
void CreateWidgets();
void SetLayoutMode(LayoutMode Mode);
@@ -117,6 +117,7 @@ protected:
void SetCategoryVisible(CategoryIndex Index, bool Visible);
void SetCategoryWidgetsVisible(CategoryWidgets& Category, bool Visible);
lcArray<lcObject*> mSelection;
lcObject* mFocusObject = nullptr;
std::array<PropertyWidgets, static_cast<int>(lcObjectPropertyId::Count)> mPropertyWidgets = {};
+82
View File
@@ -1120,6 +1120,88 @@ bool lcLight::HasKeyFrame(lcObjectPropertyId PropertyId, lcStep Time) const
return false;
}
bool lcLight::SetKeyFrame(lcObjectPropertyId PropertyId, lcStep Time, bool KeyFrame)
{
switch (PropertyId)
{
case lcObjectPropertyId::PieceId:
case lcObjectPropertyId::PieceColor:
case lcObjectPropertyId::PieceStepShow:
case lcObjectPropertyId::PieceStepHide:
case lcObjectPropertyId::CameraName:
case lcObjectPropertyId::CameraType:
case lcObjectPropertyId::CameraFOV:
case lcObjectPropertyId::CameraNear:
case lcObjectPropertyId::CameraFar:
case lcObjectPropertyId::CameraPositionX:
case lcObjectPropertyId::CameraPositionY:
case lcObjectPropertyId::CameraPositionZ:
case lcObjectPropertyId::CameraTargetX:
case lcObjectPropertyId::CameraTargetY:
case lcObjectPropertyId::CameraTargetZ:
case lcObjectPropertyId::CameraUpX:
case lcObjectPropertyId::CameraUpY:
case lcObjectPropertyId::CameraUpZ:
case lcObjectPropertyId::LightName:
case lcObjectPropertyId::LightType:
return false;
case lcObjectPropertyId::LightColor:
return mColor.SetKeyFrame(Time, KeyFrame);
case lcObjectPropertyId::LightPower:
return mPower.SetKeyFrame(Time, KeyFrame);
case lcObjectPropertyId::LightCastShadow:
return false;
case lcObjectPropertyId::LightAttenuationDistance:
return mAttenuationDistance.SetKeyFrame(Time, KeyFrame);
case lcObjectPropertyId::LightAttenuationPower:
return mAttenuationPower.SetKeyFrame(Time, KeyFrame);
case lcObjectPropertyId::LightPointSize:
case lcObjectPropertyId::LightSpotSize:
case lcObjectPropertyId::LightDirectionalSize:
case lcObjectPropertyId::LightAreaSize:
case lcObjectPropertyId::LightAreaSizeX:
case lcObjectPropertyId::LightAreaSizeY:
return mSize.SetKeyFrame(Time, KeyFrame);
case lcObjectPropertyId::LightSpotConeAngle:
return mSpotConeAngle.SetKeyFrame(Time, KeyFrame);
case lcObjectPropertyId::LightSpotPenumbraAngle:
return mSpotPenumbraAngle.SetKeyFrame(Time, KeyFrame);
case lcObjectPropertyId::LightSpotTightness:
return mSpotTightness.SetKeyFrame(Time, KeyFrame);
case lcObjectPropertyId::LightAreaShape:
return false;
case lcObjectPropertyId::LightAreaGridX:
case lcObjectPropertyId::LightAreaGridY:
return mAreaGrid.SetKeyFrame(Time, KeyFrame);
case lcObjectPropertyId::ObjectPositionX:
case lcObjectPropertyId::ObjectPositionY:
case lcObjectPropertyId::ObjectPositionZ:
return mPosition.SetKeyFrame(Time, KeyFrame);
case lcObjectPropertyId::ObjectRotationX:
case lcObjectPropertyId::ObjectRotationY:
case lcObjectPropertyId::ObjectRotationZ:
return mRotation.SetKeyFrame(Time, KeyFrame);
case lcObjectPropertyId::Count:
return false;
}
return false;
}
void lcLight::RemoveKeyFrames()
{
mPosition.RemoveAllKeys();
+2 -1
View File
@@ -214,6 +214,7 @@ public:
void BoxTest(lcObjectBoxTest& ObjectBoxTest) const override;
void DrawInterface(lcContext* Context, const lcScene& Scene) const override;
bool HasKeyFrame(lcObjectPropertyId PropertyId, lcStep Time) const override;
bool SetKeyFrame(lcObjectPropertyId PropertyId, lcStep Time, bool KeyFrame) override;
void RemoveKeyFrames() override;
void InsertTime(lcStep Start, lcStep Time);
@@ -312,7 +313,7 @@ public:
}
void CompareBoundingBox(lcVector3& Min, lcVector3& Max);
void UpdatePosition(lcStep Step);
void UpdatePosition(lcStep Step) override;
void MoveSelected(lcStep Step, bool AddKey, const lcVector3& Distance, bool FirstMove);
void Rotate(lcStep Step, bool AddKey, const lcMatrix33& RotationMatrix, const lcVector3& Center, const lcMatrix33& RotationFrame);
void CreateName(const lcArray<lcLight*>& Lights);
+2
View File
@@ -97,12 +97,14 @@ public:
virtual void SetFocused(quint32 Section, bool Focused) = 0;
virtual quint32 GetFocusSection() const = 0;
virtual void UpdatePosition(lcStep Step) = 0;
virtual quint32 GetAllowedTransforms() const = 0;
virtual lcVector3 GetSectionPosition(quint32 Section) const = 0;
virtual void RayTest(lcObjectRayTest& ObjectRayTest) const = 0;
virtual void BoxTest(lcObjectBoxTest& ObjectBoxTest) const = 0;
virtual void DrawInterface(lcContext* Context, const lcScene& Scene) const = 0;
virtual bool HasKeyFrame(lcObjectPropertyId PropertyId, lcStep Time) const = 0;
virtual bool SetKeyFrame(lcObjectPropertyId PropertyId, lcStep Time, bool KeyFrame) = 0;
virtual void RemoveKeyFrames() = 0;
virtual QString GetName() const = 0;
+60
View File
@@ -706,6 +706,66 @@ bool lcPiece::HasKeyFrame(lcObjectPropertyId PropertyId, lcStep Time) const
return false;
}
bool lcPiece::SetKeyFrame(lcObjectPropertyId PropertyId, lcStep Time, bool KeyFrame)
{
switch (PropertyId)
{
case lcObjectPropertyId::PieceId:
case lcObjectPropertyId::PieceColor:
case lcObjectPropertyId::PieceStepShow:
case lcObjectPropertyId::PieceStepHide:
case lcObjectPropertyId::CameraName:
case lcObjectPropertyId::CameraType:
case lcObjectPropertyId::CameraFOV:
case lcObjectPropertyId::CameraNear:
case lcObjectPropertyId::CameraFar:
case lcObjectPropertyId::CameraPositionX:
case lcObjectPropertyId::CameraPositionY:
case lcObjectPropertyId::CameraPositionZ:
case lcObjectPropertyId::CameraTargetX:
case lcObjectPropertyId::CameraTargetY:
case lcObjectPropertyId::CameraTargetZ:
case lcObjectPropertyId::CameraUpX:
case lcObjectPropertyId::CameraUpY:
case lcObjectPropertyId::CameraUpZ:
case lcObjectPropertyId::LightName:
case lcObjectPropertyId::LightType:
case lcObjectPropertyId::LightColor:
case lcObjectPropertyId::LightPower:
case lcObjectPropertyId::LightCastShadow:
case lcObjectPropertyId::LightAttenuationDistance:
case lcObjectPropertyId::LightAttenuationPower:
case lcObjectPropertyId::LightPointSize:
case lcObjectPropertyId::LightSpotSize:
case lcObjectPropertyId::LightDirectionalSize:
case lcObjectPropertyId::LightAreaSize:
case lcObjectPropertyId::LightAreaSizeX:
case lcObjectPropertyId::LightAreaSizeY:
case lcObjectPropertyId::LightSpotConeAngle:
case lcObjectPropertyId::LightSpotPenumbraAngle:
case lcObjectPropertyId::LightSpotTightness:
case lcObjectPropertyId::LightAreaShape:
case lcObjectPropertyId::LightAreaGridX:
case lcObjectPropertyId::LightAreaGridY:
return false;
case lcObjectPropertyId::ObjectPositionX:
case lcObjectPropertyId::ObjectPositionY:
case lcObjectPropertyId::ObjectPositionZ:
return mPosition.SetKeyFrame(Time, KeyFrame);
case lcObjectPropertyId::ObjectRotationX:
case lcObjectPropertyId::ObjectRotationY:
case lcObjectPropertyId::ObjectRotationZ:
return mRotation.SetKeyFrame(Time, KeyFrame);
case lcObjectPropertyId::Count:
return false;
}
return false;
}
void lcPiece::RemoveKeyFrames()
{
mPosition.RemoveAllKeys();
+2 -1
View File
@@ -131,6 +131,7 @@ public:
void BoxTest(lcObjectBoxTest& ObjectBoxTest) const override;
void DrawInterface(lcContext* Context, const lcScene& Scene) const override;
bool HasKeyFrame(lcObjectPropertyId PropertyId, lcStep Time) const override;
bool SetKeyFrame(lcObjectPropertyId PropertyId, lcStep Time, bool KeyFrame) override;
void RemoveKeyFrames() override;
void AddMainModelRenderMeshes(lcScene* Scene, bool Highlight, bool Fade) const;
@@ -185,7 +186,7 @@ public:
void SetPieceInfo(PieceInfo* Info, const QString& ID, bool Wait);
bool FileLoad(lcFile& file);
void UpdatePosition(lcStep Step);
void UpdatePosition(lcStep Step) override;
void MoveSelected(lcStep Step, bool AddKey, const lcVector3& Distance);
void Rotate(lcStep Step, bool AddKey, const lcMatrix33& RotationMatrix, const lcVector3& Center, const lcMatrix33& RotationFrame);
void MovePivotPoint(const lcVector3& Distance);