Esempio n. 1
0
void DIALOG_DXF_IMPORT::getPcbImportOffsets()
{
    m_PcbImportOffsetX = DoubleValueFromString( UNSCALED_UNITS, m_DxfPcbXCoord->GetValue() );
    m_PcbImportOffsetY = DoubleValueFromString( UNSCALED_UNITS, m_DxfPcbYCoord->GetValue() );

    if( m_PcbImportUnits )   // Units are inches
    {
        m_PcbImportOffsetX *= 25.4;
        m_PcbImportOffsetY *= 25.4;
    }

    return;
}
void DIALOG_CREATE_ARRAY::calculateCircularArrayProperties()
{
    wxPoint centre;

    centre.x = DoubleValueFromString( g_UserUnit, m_entryCentreX->GetValue() );
    centre.y = DoubleValueFromString( g_UserUnit, m_entryCentreY->GetValue() );

    // Find the radius, etc of the circle
    centre -= m_originalItemPosition;

    const double radius = VECTOR2I(centre.x, centre.y).EuclideanNorm();
    m_labelCircRadiusValue->SetLabelText( StringFromValue( g_UserUnit, int(radius), true ) );
}
void PANEL_PREV_3D::onMouseWheelOffset( wxMouseEvent& event )
{
    wxTextCtrl* textCtrl = (wxTextCtrl*) event.GetEventObject();

    double step = OFFSET_INCREMENT_MM;

    if( event.ShiftDown( ) )
        step = OFFSET_INCREMENT_MM_FINE;

    if( m_userUnits == INCHES )
    {
        step = OFFSET_INCREMENT_MIL/1000.0;
        if( event.ShiftDown( ) )
            step = OFFSET_INCREMENT_MIL_FINE/1000.0;
    }

    if( event.GetWheelRotation() >= 0 )
        step = -step;

    double curr_value = DoubleValueFromString( m_userUnits, textCtrl->GetValue() ) / IU_PER_MM;

    curr_value += step;
    curr_value = std::max( -MAX_OFFSET, curr_value );
    curr_value = std::min( curr_value, MAX_OFFSET );

    textCtrl->SetValue( formatOffsetValue( curr_value ) );
}
bool DIALOG_SET_GRID::getGridOrigin( wxPoint& aGridOrigin )
{
    double tmp;

    tmp = DoubleValueFromString( g_UserUnit, m_GridOriginXCtrl->GetValue() );

    // Some error checking here is a good thing.
    if( tmp < -MAX_GRID_OFFSET || tmp > MAX_GRID_OFFSET )
        return false;

    aGridOrigin.x = KiROUND( tmp );

    tmp = DoubleValueFromString( g_UserUnit, m_GridOriginYCtrl->GetValue() );

    if( tmp < -MAX_GRID_OFFSET || tmp > MAX_GRID_OFFSET )
        return false;

    aGridOrigin.y = KiROUND( tmp );

    return true;
}
/**
 * @brief rotationFromString
 * Ensure -MAX_ROTATION <= rotation <= MAX_ROTATION
 * aRotation will be normalized between -MAX_ROTATION and MAX_ROTATION
 */
static double rotationFromString( const wxString& aValue )
{
    double rotation = DoubleValueFromString( DEGREES, aValue ) / 10.0;

    if( rotation > MAX_ROTATION )
    {
        int n = rotation / MAX_ROTATION;
        rotation -= MAX_ROTATION * n;
    }
    else if( rotation < -MAX_ROTATION )
    {
        int n = -rotation / MAX_ROTATION;
        rotation += MAX_ROTATION * n;
    }

    return rotation;
}
void PANEL_PREV_3D::doIncrementRotation( wxSpinEvent& aEvent, double aSign )
{
    wxSpinButton* spinCtrl = (wxSpinButton*) aEvent.GetEventObject();
    wxTextCtrl* textCtrl = xrot;

    if( spinCtrl == m_spinYrot )
        textCtrl = yrot;
    else if( spinCtrl == m_spinZrot )
        textCtrl = zrot;

    double curr_value = DoubleValueFromString( DEGREES, textCtrl->GetValue() ) / 10.0;

    curr_value += ( ROTATION_INCREMENT * aSign );
    curr_value = std::max( -MAX_ROTATION, curr_value );
    curr_value = std::min( curr_value, MAX_ROTATION );

    textCtrl->SetValue( formatRotationValue( curr_value ) );
}
void DIALOG_MOVE_EXACT::OnOkClick( wxCommandEvent& event )
{
    m_rotation = DoubleValueFromString( DEGREES, m_rotEntry->GetValue() );

    // for the output, we only deliver a Cartesian vector
    bool ok = GetTranslationInIU( m_translation, m_polarCoords->IsChecked() );

    if ( ok )
    {
        // save the settings
        m_options.polarCoords = m_polarCoords->GetValue();
        m_xEntry->GetValue().ToDouble( &m_options.entry1 );
        m_yEntry->GetValue().ToDouble( &m_options.entry2 );
        m_rotEntry->GetValue().ToDouble( &m_options.entryRotation );

        EndModal( wxID_OK);
    }
}
void PANEL_PREV_3D::doIncrementScale( wxSpinEvent& event, double aSign )
{
    wxSpinButton* spinCtrl = (wxSpinButton*) event.GetEventObject();

    wxTextCtrl * textCtrl = xscale;

    if( spinCtrl == m_spinYscale )
        textCtrl = yscale;
    else if( spinCtrl == m_spinZscale )
        textCtrl = zscale;

    double curr_value = DoubleValueFromString( UNSCALED_UNITS, textCtrl->GetValue() );

    curr_value += ( SCALE_INCREMENT * aSign );
    curr_value = std::max( 1/MAX_SCALE, curr_value );
    curr_value = std::min( curr_value, MAX_SCALE );

    textCtrl->SetValue( formatScaleValue( curr_value ) );
}
void PANEL_PREV_3D::onMouseWheelScale( wxMouseEvent& event )
{
    wxTextCtrl* textCtrl = (wxTextCtrl*) event.GetEventObject();

    double step = SCALE_INCREMENT;

    if( event.ShiftDown( ) )
        step = SCALE_INCREMENT_FINE;

    if( event.GetWheelRotation() >= 0 )
        step = -step;

    double curr_value = DoubleValueFromString( UNSCALED_UNITS, textCtrl->GetValue() );

    curr_value += step;
    curr_value = std::max( 1/MAX_SCALE, curr_value );
    curr_value = std::min( curr_value, MAX_SCALE );

    textCtrl->SetValue( formatScaleValue( curr_value ) );
}
void PANEL_PREV_3D::onMouseWheelRot( wxMouseEvent& event )
{
    wxTextCtrl* textCtrl = (wxTextCtrl*) event.GetEventObject();

    double step = ROTATION_INCREMENT_WHEEL;

    if( event.ShiftDown( ) )
        step = ROTATION_INCREMENT_WHEEL_FINE;

    if( event.GetWheelRotation() >= 0 )
        step = -step;

    double curr_value = DoubleValueFromString( DEGREES, textCtrl->GetValue() ) / 10.0;

    curr_value += step;
    curr_value = std::max( -MAX_ROTATION, curr_value );
    curr_value = std::min( curr_value, MAX_ROTATION );

    textCtrl->SetValue( formatRotationValue( curr_value ) );
}
bool DIALOG_MOVE_EXACT::GetTranslationInIU ( wxPoint& val, bool polar )
{
    if( polar )
    {
        const int r = ValueFromTextCtrl( *m_xEntry );
        const double q = DoubleValueFromString( DEGREES, m_yEntry->GetValue() );

        val.x = r * cos( DEG2RAD( q / 10.0 ) );
        val.y = r * sin( DEG2RAD( q / 10.0 ) );
    }
    else
    {
        // direct read
        val.x = ValueFromTextCtrl( *m_xEntry );
        val.y = ValueFromTextCtrl( *m_yEntry );
    }

    // no validation to do here, but in future, you could return false here
    return true;
}
Esempio n. 12
0
double DIALOG_DXF_IMPORT::getPCBdefaultLineWidthMM()
{
    double value = DoubleValueFromString( UNSCALED_UNITS, m_textCtrlLineWidth->GetValue() );

    switch( m_PCBLineWidthUnits )
    {
        default:
        case 0:     // display units = mm
            break;

        case 1:     // display units = mil
            value *= 25.4 / 1000;
            break;

        case 2:     // display units = inch
            value *= 25.4;
            break;
    }

    return value;   // value is in mm
}
void PANEL_PREV_3D::doIncrementOffset( wxSpinEvent& event, double aSign )
{
    wxSpinButton* spinCtrl = (wxSpinButton*) event.GetEventObject();

    wxTextCtrl * textCtrl = xoff;

    if( spinCtrl == m_spinYoffset )
        textCtrl = yoff;
    else if( spinCtrl == m_spinZoffset )
        textCtrl = zoff;

    double step = OFFSET_INCREMENT_MM;

    if( m_userUnits == INCHES )
        step = OFFSET_INCREMENT_MIL/1000.0;

    double curr_value = DoubleValueFromString( m_userUnits, textCtrl->GetValue() ) / IU_PER_MM;

    curr_value += ( step * aSign );
    curr_value = std::max( -MAX_OFFSET, curr_value );
    curr_value = std::min( curr_value, MAX_OFFSET );

    textCtrl->SetValue( formatOffsetValue( curr_value ) );
}
void PANEL_PREV_3D::updateOrientation( wxCommandEvent &event )
{
    if( m_parentModelList && m_selected >= 0 && m_selected < (int) m_parentModelList->size() )
    {
        // Write settings back to the parent
        MODULE_3D_SETTINGS* modelInfo = &m_parentModelList->at( (unsigned) m_selected );

        modelInfo->m_Scale.x = DoubleValueFromString( UNSCALED_UNITS, xscale->GetValue() );
        modelInfo->m_Scale.y = DoubleValueFromString( UNSCALED_UNITS, yscale->GetValue() );
        modelInfo->m_Scale.z = DoubleValueFromString( UNSCALED_UNITS, zscale->GetValue() );

        modelInfo->m_Rotation.x = rotationFromString( xrot->GetValue() );
        modelInfo->m_Rotation.y = rotationFromString( yrot->GetValue() );
        modelInfo->m_Rotation.z = rotationFromString( zrot->GetValue() );

        modelInfo->m_Offset.x = DoubleValueFromString( m_userUnits, xoff->GetValue() ) / IU_PER_MM;
        modelInfo->m_Offset.y = DoubleValueFromString( m_userUnits, yoff->GetValue() ) / IU_PER_MM;
        modelInfo->m_Offset.z = DoubleValueFromString( m_userUnits, zoff->GetValue() ) / IU_PER_MM;

        // Update the dummy module for the preview
        UpdateDummyModule( false );
    }
}
Esempio n. 15
0
bool DIALOG_CREATE_ARRAY::TransferDataFromWindow()
{
    ARRAY_OPTIONS*  newSettings = NULL;
    wxArrayString   errors;
    const wxWindow* page = m_gridTypeNotebook->GetCurrentPage();

    if( page == m_gridPanel )
    {
        ARRAY_GRID_OPTIONS* newGrid = new ARRAY_GRID_OPTIONS();
        bool ok = true;

        // ints
        ok = ok && validateLongEntry(*m_entryNx, newGrid->m_nx, _("horizontal count"), errors);
        ok = ok && validateLongEntry(*m_entryNy, newGrid->m_ny, _("vertical count"), errors);

        newGrid->m_delta.x = m_hSpacing.GetValue();
        newGrid->m_delta.y = m_vSpacing.GetValue();

        newGrid->m_offset.x = m_hOffset.GetValue();
        newGrid->m_offset.y = m_vOffset.GetValue();

        ok = ok && validateLongEntry(*m_entryStagger, newGrid->m_stagger, _("stagger"), errors);

        newGrid->m_stagger_rows = m_radioBoxGridStaggerType->GetSelection() == 0;

        newGrid->m_horizontalThenVertical = m_radioBoxGridNumberingAxis->GetSelection() == 0;
        newGrid->m_reverseNumberingAlternate = m_checkBoxGridReverseNumbering->GetValue();

        newGrid->SetShouldNumber( m_numberingEnabled );

        if ( m_numberingEnabled )
        {
            newGrid->SetNumberingStartIsSpecified( m_rbGridStartNumberingOpt->GetSelection() == 1 );

            if( newGrid->GetNumberingStartIsSpecified() )
            {
                newGrid->m_2dArrayNumbering = m_radioBoxGridNumberingScheme->GetSelection() != 0;

                // validate from the input fields
                bool numOk = validateNumberingTypeAndOffset( *m_entryGridPriNumberingOffset,
                        *m_choicePriAxisNumbering, newGrid->m_priAxisNumType,
                        newGrid->m_numberingOffsetX, errors );

                if( newGrid->m_2dArrayNumbering )
                {
                    numOk = validateNumberingTypeAndOffset( *m_entryGridSecNumberingOffset,
                                    *m_choiceSecAxisNumbering, newGrid->m_secAxisNumType,
                                    newGrid->m_numberingOffsetY, errors )
                            && numOk;
                }

                ok = ok && numOk;
            }
            else
            {
                // artificial linear numeric scheme from 1
                newGrid->m_2dArrayNumbering = false;
                newGrid->m_priAxisNumType = ARRAY_OPTIONS::NUMBERING_TYPE_T::NUMBERING_NUMERIC;
                newGrid->m_numberingOffsetX = 1; // Start at "1"
            }
        }

        // Only use settings if all values are good
        if( ok )
            newSettings = newGrid;
        else
            delete newGrid;
    }
    else if( page == m_circularPanel )
    {
        ARRAY_CIRCULAR_OPTIONS* newCirc = new ARRAY_CIRCULAR_OPTIONS();
        bool ok = true;

        newCirc->m_centre.x = m_hCentre.GetValue();
        newCirc->m_centre.y = m_vCentre.GetValue();
        newCirc->m_angle = DoubleValueFromString( DEGREES, m_entryCircAngle->GetValue() );

        ok = ok && validateLongEntry(*m_entryCircCount, newCirc->m_nPts, _("point count"), errors);

        newCirc->m_rotateItems = m_entryRotateItemsCb->GetValue();
        newCirc->SetShouldNumber( m_numberingEnabled );

        if ( m_numberingEnabled )
        {
            newCirc->SetNumberingStartIsSpecified( m_rbCircStartNumberingOpt->GetSelection() == 1 );

            if( newCirc->GetNumberingStartIsSpecified() )
            {
                newCirc->m_numberingType = ARRAY_OPTIONS::NUMBERING_NUMERIC;

                ok = ok
                     && validateLongEntry( *m_entryCircNumberingStart, newCirc->m_numberingOffset,
                                _( "numbering start" ), errors );
            }
            else
            {
                // artificial linear numeric scheme from 1
                newCirc->m_numberingOffset = 1; // Start at "1"
            }
        }

        // Only use settings if all values are good
        if( ok )
            newSettings = newCirc;
        else
            delete newCirc;
    }

    // If we got good settings, send them out and finish
    if( newSettings )
    {
        delete m_settings;

        // assign pointer and ownership here
        m_settings = newSettings;
        m_cfg_persister.ReadConfigFromControls();

        return true;
    }
    else
    {
        wxString errorStr;

        if( errors.IsEmpty() )
            errorStr = _("Bad parameters");
        else
            errorStr = boost::algorithm::join( errors, "\n" );

        wxMessageBox( errorStr );
        return false;
    }
}
void DIALOG_CREATE_ARRAY::OnOkClick( wxCommandEvent& event )
{
    ARRAY_OPTIONS* newSettings = NULL;

    const wxWindow* page = m_gridTypeNotebook->GetCurrentPage();

    if( page == m_gridPanel )
    {
        ARRAY_GRID_OPTIONS* newGrid = new ARRAY_GRID_OPTIONS();
        bool ok = true;

        // ints
        ok  = ok && m_entryNx->GetValue().ToLong( &newGrid->m_nx );
        ok  = ok && m_entryNy->GetValue().ToLong( &newGrid->m_ny );

        newGrid->m_delta.x = DoubleValueFromString( g_UserUnit, m_entryDx->GetValue() );
        newGrid->m_delta.y = DoubleValueFromString( g_UserUnit, m_entryDy->GetValue() );

        newGrid->m_offset.x = DoubleValueFromString( g_UserUnit, m_entryOffsetX->GetValue() );
        newGrid->m_offset.y = DoubleValueFromString( g_UserUnit, m_entryOffsetY->GetValue() );

        ok = ok && m_entryStagger->GetValue().ToLong( &newGrid->m_stagger );

        newGrid->m_stagger_rows = m_radioBoxGridStaggerType->GetSelection() == 0;

        newGrid->m_horizontalThenVertical = m_radioBoxGridNumberingAxis->GetSelection() == 0;
        newGrid->m_reverseNumberingAlternate = m_checkBoxGridReverseNumbering->GetValue();

        newGrid->m_2dArrayNumbering = m_radioBoxGridNumberingScheme->GetSelection() != 0;

        // this is only correct if you set the choice up according to the enum size and order
        ok = ok && m_choicePriAxisNumbering->GetSelection() < NUMBERING_TYPE_Max
             && m_choiceSecAxisNumbering->GetSelection() < NUMBERING_TYPE_Max;

        // mind undefined casts to enums (should not be able to happen)
        if( ok )
        {
            newGrid->m_priAxisNumType =
                (ARRAY_NUMBERING_TYPE_T) m_choicePriAxisNumbering->GetSelection();
            newGrid->m_secAxisNumType =
                (ARRAY_NUMBERING_TYPE_T) m_choiceSecAxisNumbering->GetSelection();
        }

        // Work out the offsets for the numbering
        ok = ok && getNumberingOffset(
                m_entryGridPriNumberingOffset->GetValue().ToStdString(),
                newGrid->m_priAxisNumType, newGrid->m_numberingOffsetX );

        if( newGrid->m_2dArrayNumbering )
            ok = ok && getNumberingOffset(
                    m_entryGridSecNumberingOffset->GetValue().ToStdString(),
                    newGrid->m_secAxisNumType, newGrid->m_numberingOffsetY );

        newGrid->m_shouldRenumber = m_checkBoxGridRestartNumbering->GetValue();

        // Only use settings if all values are good
        if( ok )
            newSettings = newGrid;
        else
            delete newGrid;
    }
    else if( page == m_circularPanel )
    {
        ARRAY_CIRCULAR_OPTIONS* newCirc = new ARRAY_CIRCULAR_OPTIONS();
        bool ok = true;

        newCirc->m_centre.x = DoubleValueFromString( g_UserUnit, m_entryCentreX->GetValue() );
        newCirc->m_centre.y = DoubleValueFromString( g_UserUnit, m_entryCentreY->GetValue() );

        newCirc->m_angle = DoubleValueFromString( DEGREES, m_entryCircAngle->GetValue() );
        ok = ok && m_entryCircCount->GetValue().ToLong( &newCirc->m_nPts );

        newCirc->m_rotateItems = m_entryRotateItemsCb->GetValue();

        newCirc->m_shouldRenumber = m_checkBoxCircRestartNumbering->GetValue();

        // This is only correct if you set the choice up according to the enum size and order
        ok = ok && m_choiceCircNumberingType->GetSelection() < NUMBERING_TYPE_Max;

        // Mind undefined casts to enums (should not be able to happen)
        if( ok )
            newCirc->m_numberingType =
                (ARRAY_NUMBERING_TYPE_T) m_choiceCircNumberingType->GetSelection();

        ok = ok && m_entryCircNumberingStart->GetValue().ToLong( &newCirc->m_numberingOffset );

        // Only use settings if all values are good
        if( ok )
            newSettings = newCirc;
        else
            delete newCirc;
    }

    // If we got good settings, send them out and finish
    if( newSettings )
    {
        delete *m_settings;

        // assign pointer and ownership here
        *m_settings = newSettings;

        ReadConfigFromControls();

        EndModal( wxID_OK );
    }
}
 double GetYOrg()
 {
     return DoubleValueFromString( UNSCALED_UNITS, m_STEP_Yorg->GetValue() );
 }
 double GetYRef()
 {
     return DoubleValueFromString( UNSCALED_UNITS, m_VRML_Yref->GetValue() );
 }
void DIALOG_DXF_IMPORT::GetPCBGridOffsets( double &aXOffset, double &aYOffset )
{
    aXOffset = DoubleValueFromString( UNSCALED_UNITS, m_DXFPCBXCoord->GetValue() );
    aYOffset = DoubleValueFromString( UNSCALED_UNITS, m_DXFPCBYCoord->GetValue() );
    return;
}
void DIALOG_CREATE_ARRAY::OnOkClick( wxCommandEvent& event )
{
    ARRAY_OPTIONS* newSettings = NULL;

    wxArrayString errorStrs;

    const wxWindow* page = m_gridTypeNotebook->GetCurrentPage();

    if( page == m_gridPanel )
    {
        ARRAY_GRID_OPTIONS* newGrid = new ARRAY_GRID_OPTIONS();
        bool ok = true;

        // ints
        ok = ok && validateLongEntry(*m_entryNx, newGrid->m_nx, _("horizontal count"),
                          errorStrs);
        ok = ok && validateLongEntry(*m_entryNy, newGrid->m_ny, _("vertical count"),
                          errorStrs);


        newGrid->m_delta.x = DoubleValueFromString( g_UserUnit, m_entryDx->GetValue() );
        newGrid->m_delta.y = DoubleValueFromString( g_UserUnit, m_entryDy->GetValue() );

        newGrid->m_offset.x = DoubleValueFromString( g_UserUnit, m_entryOffsetX->GetValue() );
        newGrid->m_offset.y = DoubleValueFromString( g_UserUnit, m_entryOffsetY->GetValue() );

        ok = ok && validateLongEntry(*m_entryStagger, newGrid->m_stagger, _("stagger"),
                          errorStrs);

        newGrid->m_stagger_rows = m_radioBoxGridStaggerType->GetSelection() == 0;

        newGrid->m_horizontalThenVertical = m_radioBoxGridNumberingAxis->GetSelection() == 0;
        newGrid->m_reverseNumberingAlternate = m_checkBoxGridReverseNumbering->GetValue();

        newGrid->m_shouldNumber = m_numberingEnabled;

        if ( m_numberingEnabled )
        {
            newGrid->m_2dArrayNumbering = m_radioBoxGridNumberingScheme->GetSelection() != 0;

            bool numOk = validateNumberingTypeAndOffset(
                    *m_entryGridPriNumberingOffset, *m_choicePriAxisNumbering,
                    newGrid->m_priAxisNumType, newGrid->m_numberingOffsetX,
                    errorStrs );

            if( newGrid->m_2dArrayNumbering )
            {
                numOk = validateNumberingTypeAndOffset(
                        *m_entryGridSecNumberingOffset, *m_choiceSecAxisNumbering,
                        newGrid->m_secAxisNumType, newGrid->m_numberingOffsetY,
                        errorStrs ) && numOk;
            }

            ok = ok && numOk;

            newGrid->m_numberingStartIsSpecified = m_rbGridStartNumberingOpt->GetSelection() == 1;
        }

        // Only use settings if all values are good
        if( ok )
            newSettings = newGrid;
        else
            delete newGrid;
    }
    else if( page == m_circularPanel )
    {
        ARRAY_CIRCULAR_OPTIONS* newCirc = new ARRAY_CIRCULAR_OPTIONS();
        bool ok = true;

        newCirc->m_centre.x = DoubleValueFromString( g_UserUnit, m_entryCentreX->GetValue() );
        newCirc->m_centre.y = DoubleValueFromString( g_UserUnit, m_entryCentreY->GetValue() );

        newCirc->m_angle = DoubleValueFromString( DEGREES, m_entryCircAngle->GetValue() );

        ok = ok && validateLongEntry(*m_entryCircCount, newCirc->m_nPts,
                                     _("point count"), errorStrs);

        newCirc->m_rotateItems = m_entryRotateItemsCb->GetValue();

        newCirc->m_shouldNumber = m_numberingEnabled;

        if ( m_numberingEnabled )
        {
            newCirc->m_numberingStartIsSpecified = m_rbCircStartNumberingOpt->GetSelection() == 1;
            newCirc->m_numberingType = NUMBERING_NUMERIC;

            ok = ok && validateLongEntry(*m_entryCircNumberingStart,
                                         newCirc->m_numberingOffset,
                                         _("numbering start"), errorStrs);
        }

        // Only use settings if all values are good
        if( ok )
            newSettings = newCirc;
        else
            delete newCirc;
    }

    // If we got good settings, send them out and finish
    if( newSettings )
    {
        delete m_settings;

        // assign pointer and ownership here
        m_settings = newSettings;
        ReadConfigFromControls();

        EndModal( wxID_OK );
    }
    else
    {
        wxString errorStr;

        if( errorStrs.IsEmpty() )
            errorStr = _("Bad parameters");
        else
            errorStr = boost::algorithm::join( errorStrs, "\n" );

        wxMessageBox( errorStr );
    }
}