You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ktechlab/src/electronics/components/rotoswitch.cpp

318 lines
9.7 KiB

/***************************************************************************
* Copyright (C) 2005 by John Myers *
* electronerd@electronerdia.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
***************************************************************************/
#include "rotoswitch.h"
#include "canvasitemparts.h"
#include "ecnode.h"
#include "libraryitem.h"
#include "switch.h"
#include <tdelocale.h>
#include <tqpainter.h>
#include <cmath>
#include <assert.h>
#include <kdebug.h>
//BEGIN class ECRotoSwitch
Item* ECRotoSwitch::construct( ItemDocument *itemDocument, bool newItem, const char *id )
{
return new ECRotoSwitch( (ICNDocument*)itemDocument, newItem, id );
}
LibraryItem* ECRotoSwitch::libraryItem()
{
return new LibraryItem(
TQString("ec/roto_switch"),
i18n("Rotary"),
i18n("Switches"),
"rotary.png",
LibraryItem::lit_component,
ECRotoSwitch::construct );
}
ECRotoSwitch::ECRotoSwitch( ICNDocument *icnDocument, bool newItem, const char *id )
: Component( icnDocument, newItem, id ? id : "roto_switch" ),
m_numPositions(0)
{
// m_name = i18n("Rotary Switch(WIP)");
m_name = i18n("Rotary Switch");
m_desc = i18n("Rotary Switch"); ///< \todo better description for rotoswitch
TQPointArray pa;
pa.makeArc( -_pinInnerRadius, -_pinInnerRadius, 2*_pinInnerRadius, 2*_pinInnerRadius , 0, 16*360 );
setItemPoints( pa );
//setSize( -64, -64, 128, 128 );
//half the side length of the buttons
int buttonRadius = 10;
addButton( "go_left", TQRect( -_pinOuterRadius/3-buttonRadius, _pinOuterRadius-3*buttonRadius, 2*buttonRadius, 2*buttonRadius ), "<", false );
addButton( "go_right", TQRect(_pinOuterRadius/3-buttonRadius, _pinOuterRadius-3*buttonRadius, 2*buttonRadius, 2*buttonRadius ), ">", false );
/*Variant * v = createProperty( "button_map", Variant::Type::String );
v->setCaption( i18n("Button Map") );
v->setAdvanced(true);
const TQString defButtonMap("SSSSSSSSSSSM");
v->setValue(defButtonMap);
*/
Variant * v = createProperty( "num_positions", Variant::Type::Int );
v->setCaption( i18n("Number of Positions") );
v->setAdvanced(false);
v->setValue(6);
v->setMinValue(3);
m_inNode = createPin(0,height()/2,270,"in");
v = createProperty( "bounce", Variant::Type::Bool );
v->setCaption("Bounce");
v->setAdvanced(true);
v->setValue(false);
v = createProperty( "bounce_period", Variant::Type::Double );
v->setCaption("Bounce Period");
v->setAdvanced(true);
v->setUnit("s");
v->setValue(5e-3);
v = createProperty( "cur_position", Variant::Type::Int );
v->setHidden( true );
v->setValue( 0 );
//v = createProperty( "left_momentary", Variant::Type::Bool );
//v->setCaption(i18n("Left Momentary" ) );
//v->setValue(false);
}
ECRotoSwitch::~ECRotoSwitch()
{
}
void ECRotoSwitch::dataChanged()
{
bool bounce = dataBool("bounce");
int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3);
m_curPosition = dataInt( "cur_position" );
setUpSwitches();
if(m_positions[m_curPosition].posSwitch->state() != Switch::Closed)
{
m_positions[m_curPosition].posSwitch->setState(Switch::Closed);
}
for(int i = 0; i < m_numPositions; i++)
{
m_positions[i].posSwitch->setBounce( bounce, bouncePeriod_ms );
}
}
inline int roundTo10(int a){return ((a/10)+(a%10<5?0:1))*10;}
void ECRotoSwitch::drawShape( TQPainter &p )
{
initPainter(p);
int cx = static_cast<int>(x());
int cy = static_cast<int>(y());
const int rotorRadius = 5;
//draw the rotor
p.drawEllipse(cx - rotorRadius, cy-rotorRadius, 2*rotorRadius, 2*rotorRadius);
//and its connection
p.drawLine(cx, cy+rotorRadius, cx, cy+_pinInnerRadius);
//draw the output positions
double angleBetweenPositions = (4*M_PI/3)/(m_numPositions - 1);
//kdDebug() << "drawShape: " << bigRadius << " " << angleBetweenPositions << endl;
/// \internal \brief Round to the nearest multiple of 8
#define round_8(a) (((a) > 0) ? int(((a)+4)/8)*8 : int(((a)-4)/8)*8)
for(int i = 0; i < m_numPositions ; i++)
{
double angle = (7*M_PI/6) - (i * angleBetweenPositions);
int contactX = static_cast<int>(_contactRingRadius * cos(angle));
int contactY = static_cast<int>(_contactRingRadius * sin(angle));
p.drawEllipse(cx+contactX-_contactRadius, cy-contactY-_contactRadius, 2*_contactRadius, 2*_contactRadius);
int pinX, pinY;
switch(m_positions[i].pinAngle)
{
case 180:
pinX = _pinInnerRadius;
pinY = round_8(contactY);
break;
case 90:
pinX = round_8(contactX);
pinY = _pinInnerRadius;
break;
case 0:
pinX = -_pinInnerRadius;
pinY = round_8(contactY);
break;
default:
assert(!"Bad pin angle");
}
p.drawLine(cx+contactX, cy-contactY, cx+pinX, cy-pinY);
//kdDebug() << contactX <<", "<< contactY <<endl;
}
#undef round_8
//draw the connection to the selected position
double angle = (7*M_PI/6) - (m_curPosition * angleBetweenPositions);
int contactX = static_cast<int>(_contactRingRadius * cos(angle));
int contactY = static_cast<int>(_contactRingRadius * sin(angle));
int rotorX = static_cast<int>(rotorRadius * cos(angle));
int rotorY = static_cast<int>(rotorRadius * sin(angle));
p.drawLine(cx+rotorX, cy-rotorY, cx+contactX, cy-contactY);
deinitPainter(p);
}
void ECRotoSwitch::buttonStateChanged( const TQString & id, bool state )
{
SwitchPosition& curSP = m_positions[m_curPosition];
int nextPos = m_curPosition;
if(m_numPositions < 2)
{
return;
}
if(!state) //release
{
if(!curSP.isMomentary)
{
return;
}
if(m_curPosition == 0)
{
nextPos = m_curPosition + 1;
}
else if(m_curPosition == m_numPositions - 1)
{
nextPos = m_curPosition - 1;
}
}
else //press
{
if(id == "go_left" && m_curPosition > 0)
{
nextPos = m_curPosition - 1;
}
else if(id == "go_right" && m_curPosition < m_numPositions - 1)
{
nextPos = m_curPosition + 1;
}
}
if(nextPos != m_curPosition)
{
SwitchPosition& nextSP = m_positions[nextPos];
curSP.posSwitch->setState(Switch::Open);
nextSP.posSwitch->setState(Switch::Closed);
m_curPosition = nextPos;
property( "cur_position" )->setValue( m_curPosition );
}
}
/*!
Set up the switches according to the button_map
*
*
*/
void ECRotoSwitch::setUpSwitches()
{
if( dataInt("num_positions") == m_numPositions )
{
// number of positions didn't change, so we don't have to do anything.
return;
}
//this uses the _old_ value of m_numPositions!
for(int i=0; i<m_numPositions; i++)
{
SwitchPosition& sp = m_positions[i];
TQString pinName = TQString("pin_%1").arg(i);
removeNode(pinName);
removeSwitch(sp.posSwitch);
}
m_numPositions = dataInt("num_positions");
if(m_curPosition >= m_numPositions )
{
setActivePosition( m_numPositions - 1 );
}
m_positions.clear();///\todo readjust old pins
m_positions.reserve(m_numPositions);
double angleBetweenPositions = (4*M_PI/3)/(m_numPositions - 1);
//kdDebug() << "setUpSwitches: " << bigRadius << " " << angleBetweenPositions << endl;
for( int i = 0; i < m_numPositions; i++)
{
double angle = (7*M_PI/6) - (i * angleBetweenPositions);
int contactX = static_cast<int>(_contactRingRadius * cos(angle));
int contactY = static_cast<int>(_contactRingRadius * sin(angle));
SwitchPosition sp;
if(angle > 3*M_PI/4)
{
sp.pinAngle = 0;
contactX = -_pinOuterRadius;
}
else if(angle > M_PI/4)
{
sp.pinAngle = 90;
contactY=_pinOuterRadius;
}
else
{
sp.pinAngle = 180;
contactX = _pinOuterRadius;
}
// kdDebug() << contactX <<", "<< contactY <<endl;
sp.node = createPin(contactX,-contactY,sp.pinAngle,TQString("pin_%1").arg(i));
sp.posSwitch = createSwitch(m_inNode, sp.node, true);
sp.isMomentary = false;//(map[i] == 'M');
m_positions.push_back(sp);
}
updateAttachedPositioning();
// redraw ourself
setChanged();
}
/*!
* Set the current position to \c newPosition updating the state of the switch.
* \c m_curPosition must reference a valid position to switch away from
*
* \param newPosition the position to switch to
*/
void ECRotoSwitch::setActivePosition(int newPosition)
{
SwitchPosition& curSP = m_positions[m_curPosition];
SwitchPosition& nextSP = m_positions[newPosition];
curSP.posSwitch->setState(Switch::Open);
nextSP.posSwitch->setState(Switch::Closed);
m_curPosition = newPosition;
property( "cur_position" )->setValue( m_curPosition );
}
//END class ECRotoSwitch