I'm trying to write button class, one that would switch between on and off states using boolean function.
Switch should happen after if(mousePressed). I can get it to work one way, but I'm lost while trying to get it to switch back to previous state.
When I change one of the if(mousePressed) to if(keyPressed) everything works just fine.
Thank you for your time good Sir.
bclass button;
int offset = 20;
void setup() {
rectMode(CORNERS);
noStroke();
size(300, 100);
button = new bclass(offset, offset, width-offset, height-offset);
}
void draw() {
button.display();
button.state(mouseX, mouseY);
}
class bclass {
float x;
float y;
float w;
float h;
boolean state = false;
bclass(float x_, float y_, float w_, float h_) {
x = x_;
y = y_;
w = w_;
h = h_;
}
void display() {
if (state) {
fill(255, 0, 0);
} else {
fill(0);
}
rect(x, y, w, h);
}
void state(int mx, int my) {
if (!state) {
if (mousePressed) {
if (mx > x && mx < x + w) {
state = true;
}
}
}
if (state) {
if (mousePressed) {
if (mx > x && mx < x + w) {
state = false;
}
}
}
}
}
Change your state method to:
void state(int mx, int my) {
if (mousePressed) {
if (mx > x && mx < x + w) {
state = !state; // change state to opposite value
delay(100); // delay 100ms (mousePressed lasts some millis)
}
}
}
But better solution will be to use Processing's mouseClicked() event:
public void draw() {
button.display();
}
public void mouseClicked() {
button.state(mouseX, mouseY);
}
class bclass {
...
void state(int mx, int my) {
if (mx > x && mx < x + w) {
state = !state;
}
}
}
In this solution button.state() is called only from mouseClicked(), and mouseClicked() is called by Processing when it discovers such event.
Related
Has anyone tried to use coroutine to solve stack overflow caused by too deep recursive function call? according to the document on coroutines, the coroutine state will be saved on heap instead of on stack, which could have the potential to avoid the limitation imposed by the limited stack size and thus provide a way to solve the stack overflow issue in a generic way. i have tried with some code but it looks like the stack over flow issue persists. anyone has any tips/advice to share? or point me to some tutorial? thanks in advance.
// file main
#include "RecursiveCall.h"
// coroutine
static ReturnObject DoIntegration(Context& ctx, ReturnObject::promise_type* parent, double x_n)
{
double* dummyA = new double[(int)((x_n + 1) * 2)]; // an effort to prevent heap allocation from "optimized out"
co_await AwaitableBase(ctx, parent, x_n);
ctx._dummyVec.push_back(dummyA); // part of the effort to prevent heap allocation from "optimized out"
}
// caller
static double Invoke(Context& ctx, ReturnObject::promise_type* parent, double x_n)
{
auto ret = DoIntegration(ctx, parent, x_n);
std::coroutine_handle<ReturnObject::promise_type> h = ret._coroH;
auto p = h.promise();
while (!h.done())
{
if (p.AreChildrenReady())
{
h();
break;
}
}
return p._area;
}
bool AwaitableBase::await_suspend(std::coroutine_handle<PromiseType> h)
{
_promise = &h.promise();
if (_parent)
{
_parent->RegisterChild(h);
}
if (_x_n <= _ctx._b)
{
_promise->_x_n = 0.0;
_promise->_area = 0.0;
return false;
}
_promise->_area = GetArea(_x_n, _ctx._incr);
double newX = _x_n - _ctx._incr;
_promise->_x_n = newX;
double area = Invoke(_ctx, &h.promise(), newX);
//post calculation
_promise->_area += area;
return true;
}
double CallRecursive(double x0, double x_n, double incr)
{
Context ctx{ x0, incr };
return Invoke(ctx, nullptr, x_n);
}
int main()
{
double x0 = 0.0;
double x_n = 4.5;
double incr = 0.5; // no stackoveflow
//double incr = 0.0015; // stack oveflow
auto area = CallRecursive(x0, x_n, incr);
std::cout << "integrated result: " << area << "\n";
}
// file RecrusiveCall.h
#include <coroutine>
#include <exception>
#include <map>
#include <iostream>
#include <vector>
/* integration certainly can and should be done in a sequencial way in real world. but here is just use it as a simple example of recursive call, so the integration is implemented as a recursive function call and is done from high limit of x to the lower limit */
static double GetY(double x)
{
using CurvePoint = std::pair<double, double>;
constexpr CurvePoint curve[10] = { {0.0, 1.0}, {0.5, 1.2}, {1.0, 1.0}, {1.5, 1.2}, {2.0, 1.0},
{2.5, 1.2}, {3.0, 1.0}, {3.5, 1.2}, {4.0, 1.0}, {4.5, 1.2} };
if (x < curve[0].first || x > curve[9].first)
return 0.0;
CurvePoint newPoint;
const auto p1 = std::lower_bound(&curve[0], &curve[10], x, [](const auto& a, const auto& b) constexpr { return a.first < b; });
// check for special cases: first
const auto p0 = p1 - 1;
return (p1->second - p0->second) * (x - p0->first) / (p1->first - p0->first) + p0->second;
}
static double GetArea(double end, double incr)
{
return (GetY(end) + GetY(end - incr)) * 0.5 * incr;
}
struct Context
{
double _b; // lower limit of the integration range
double _incr; // increment steplength
std::vector<double*> _dummyVec; // effort to prevent heap allocation from being optimzed out
~Context()
{
for (auto p : _dummyVec)
delete p;
}
};
struct ReturnObject
{
struct promise_type
{
using Handle = std::coroutine_handle<promise_type>;
ReturnObject get_return_object() {
return { std::coroutine_handle<promise_type>::from_promise(*this) };
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() {}
void return_void() {}
void RegisterChild(Handle& childH)
{
_children.push_back(childH);
}
bool AreChildrenReady()
{
for (auto c : _children)
{
if (!c.done())
return false;
}
return true;
}
double GetValue() const { return _area; }
std::vector<Handle> _children;
double _area{ 0 };
double _x_n{ 0 };
};
ReturnObject(promise_type::Handle coro) : _coroH(coro)
{
}
operator std::coroutine_handle<promise_type>() const { return _coroH; }
// A coroutine_handle<promise_type> converts to coroutine_handle<>
operator std::coroutine_handle<>() const { return _coroH; }
std::coroutine_handle<promise_type> _coroH;
};
struct AwaitableBase
{
typedef Context Ctx;
using PromiseType = ReturnObject::promise_type; // todo: remove
bool await_ready()
{
return false;
}
bool await_suspend(std::coroutine_handle<PromiseType> h);
PromiseType* await_resume()
{
return _promise;
}
AwaitableBase(Ctx& ctx, PromiseType* parent, double x_n) : _ctx(ctx), _x_n(x_n), _parent(parent)
{
}
~AwaitableBase()
{
}
Ctx& _ctx;
PromiseType* _parent{ nullptr };
PromiseType* _promise{ nullptr };
double _x_n{ 0.0 };
};
no.
the coroutine's stack frame remains allocated. what's pushed to heap (as custom handle struct) is minimal register state plus struct data, to later resume the stack frame.
that is how you can access all local variables after resuming where you left off.
In my window there is a circle. I have implemented the void MouseClicked() method to take effect on the mouse click event. That means only clicking inside the circle should change the color of the circle and do the corresponding operation.
But the problem is wherever I click (even outside the circle), it changes the color of the circle. So I understand that the mouseClicked() method isn't stable. How do I fix this?
My code in processing:
int colorValue = 0;
void setup() {
size(450, 255);
background(204);
}
void draw() {
fill(colorValue);
ellipse(56, 46, 55, 55);
}
void mouseClicked() {
if (colorValue == 0) {
colorValue = 255;
} else {
colorValue = 0;
}
}
You aren't doing any check for whether the mouse is in the circle. You could use the dist() function to help with that:
int colorValue = 0;
float circleX = 56;
float circleY = 46;
float circleR = 55;
void setup() {
size(450, 255);
background(204);
ellipseMode(RADIUS);
}
void draw() {
fill(colorValue);
ellipse(circleX, circleY, circleR, circleR);
}
void mouseClicked() {
if(dist(mouseX, mouseY, circleX, circleY) < circleR){
if (colorValue == 0) {
colorValue = 255;
} else {
colorValue = 0;
}
}
}
I am using processing and I would like to create a game where the player is in a room and if they hit a wall they stop moving like a normal wall. However I don't really have any idea how to do this. With my current method once they hit the wall they can't move on the x axis anymore. Any help would be greatly appreciated.
PVector playerPosition;
PVector barrierPosition;
float velocity = 4;
boolean goLeft=false;
boolean goRight=false;
boolean goUp=false;
boolean goDown=false;
float playerWidth = 10;
float playerHeight = 10;
float barrierWidth = 10;
float barrierHeight = 600;
void setup() {
size(800,600);
rectMode(CORNER);
playerPosition = new PVector(200,200);
barrierPosition = new PVector(0,0);
}
void draw() {
background(255);
drawPlayer();
movePlayerPosition();
drawBarrier();
checkCollide();
}
//check for collision with barrier then stop player moving
void checkCollide() {
if (playerPosition.y <= barrierHeight + playerHeight && playerPosition.x <= barrierWidth + playerWidth)
{
println("COLLIDED");
playerPosition.x = barrierPosition.x + 10;
}
}
void drawPlayer() {
fill(255,0,0);
rect(playerPosition.x,playerPosition.y,playerWidth,playerHeight);
}
void drawBarrier() {
fill(0);
rect(barrierPosition.x,barrierPosition.y,barrierWidth,barrierHeight);
}
void movePlayerPosition() {
//moves up and down
if (goUp && playerPosition.y > 0)
{
playerPosition.y = playerPosition.y - velocity;
} else if (goDown && playerPosition.y < 598-playerHeight)
{
playerPosition.y = playerPosition.y + velocity;
}
//moves right and left
if (goLeft && playerPosition.x > 0)
{
playerPosition.x = playerPosition.x -velocity;
} else if (goRight && playerPosition.x <800-playerWidth)
{
playerPosition.x = playerPosition.x + velocity;
}
}
//if the keys are pressed move in that direction
void keyPressed() {
if (key == 'w') {
goUp=true;
} else if (key == 'a') {
goLeft=true;
} else if (key == 's') {
goDown=true;
} else if (key == 'd') {
goRight=true;
}
}
//if the keys are released stop moving
void keyReleased() {
if (key == 'w') {
goUp=false;
} else if (key == 'a') {
goLeft=false;
} else if (key == 's') {
goDown=false;
} else if (key == 'd') {
goRight=false;
}
}
Simple answer first; Your checkForCollide method should return a boolean instead of been void, then you can use this method with your movePlayerPosition one and only perform the X axis movement when there has been no collision.
Now, in a more complete answer; You should study a little more about the subject, there are a lot of patterns and libraries to better and easier handle this topic.
I recommend you the Nature of code, you can get this pdf for free and you have a lot about the use of physic libraries from chapter 5 and on, also lots of samples about programming games.
Hope this helps and good luck.
Regards Jose
You're so very close!
After this line:
println("COLLIDED");
you set the new coordinates like so:
playerPosition.x = barrierPosition.x + 10;
This means after the first collision takes places, the x position will always be set to barrierPosition.x + 10;
What you probably want to do is move the player by 10 pixels, but relative to it's present position:
playerPosition.x = playerPosition.x + 10;
or:
playerPosition.x += 10;
Have fun!
For reference, your full code with a few characters modified:
PVector playerPosition;
PVector barrierPosition;
float velocity = 4;
boolean goLeft=false;
boolean goRight=false;
boolean goUp=false;
boolean goDown=false;
float playerWidth = 10;
float playerHeight = 10;
float barrierWidth = 10;
float barrierHeight = 600;
void setup() {
size(800,600);
rectMode(CORNER);
playerPosition = new PVector(200,200);
barrierPosition = new PVector(0,0);
}
void draw() {
background(255);
drawPlayer();
movePlayerPosition();
drawBarrier();
checkCollide();
}
//check for collision with barrier then stop player moving
void checkCollide() {
if (playerPosition.y <= barrierHeight + playerHeight && playerPosition.x <= barrierWidth + playerWidth)
{
println("COLLIDED");
playerPosition.x += 10;
}
}
void drawPlayer() {
fill(255,0,0);
rect(playerPosition.x,playerPosition.y,playerWidth,playerHeight);
}
void drawBarrier() {
fill(0);
rect(barrierPosition.x,barrierPosition.y,barrierWidth,barrierHeight);
}
void movePlayerPosition() {
//moves up and down
if (goUp && playerPosition.y > 0)
{
playerPosition.y = playerPosition.y - velocity;
} else if (goDown && playerPosition.y < 598-playerHeight)
{
playerPosition.y = playerPosition.y + velocity;
}
//moves right and left
if (goLeft && playerPosition.x > 0)
{
playerPosition.x = playerPosition.x -velocity;
} else if (goRight && playerPosition.x <800-playerWidth)
{
playerPosition.x = playerPosition.x + velocity;
}
}
//if the keys are pressed move in that direction
void keyPressed() {
if (key == 'w') {
goUp=true;
} else if (key == 'a') {
goLeft=true;
} else if (key == 's') {
goDown=true;
} else if (key == 'd') {
goRight=true;
}
}
//if the keys are released stop moving
void keyReleased() {
if (key == 'w') {
goUp=false;
} else if (key == 'a') {
goLeft=false;
} else if (key == 's') {
goDown=false;
} else if (key == 'd') {
goRight=false;
}
}
Is there any way to allow the user to interactively resize the columns when headers are hidden?
You can install an event filter on the table's viewport and implement needed behavior manually. Below is a sample implementation.
Header:
#include <QTableView>
class Table_cell_resizer : public QObject {
Q_OBJECT
public:
explicit Table_cell_resizer(QTableView *view = 0);
protected:
bool eventFilter(QObject* object, QEvent* event);
private:
QTableView* m_view;
//max distance between mouse and a cell, small enough to trigger resize
int m_sensibility;
//variables for saving state while dragging
bool m_drag_in_progress;
Qt::Orientation m_drag_orientation;
int m_drag_section;
int m_drag_previous_pos;
// check if mouse_pos is around right or bottom side of a cell
// (depending on orientation)
// and return the index of that cell if found
QModelIndex index_resizable(QPoint mouse_pos, Qt::Orientation orientation);
};
Source:
#include "Table_cell_resizer.h"
#include <QMouseEvent>
#include <QHeaderView>
Table_cell_resizer::Table_cell_resizer(QTableView* view) :
QObject(view), m_view(view)
{
m_view->viewport()->installEventFilter(this);
m_view->viewport()->setMouseTracking(true);
m_sensibility = 5;
m_drag_in_progress = false;
}
bool Table_cell_resizer::eventFilter(QObject* object, QEvent* event) {
if (object == m_view->viewport()) {
QMouseEvent* mouse_event = dynamic_cast<QMouseEvent*>(event);
if (mouse_event) {
if (mouse_event->type() == QEvent::MouseMove) {
if (m_drag_in_progress) { // apply dragging
int delta;
QHeaderView* header_view;
if (m_drag_orientation == Qt::Vertical) {
delta = mouse_event->pos().y() - m_drag_previous_pos;
header_view = m_view->verticalHeader();
m_drag_previous_pos = mouse_event->pos().y();
} else if (m_drag_orientation == Qt::Horizontal) {
delta = mouse_event->pos().x() - m_drag_previous_pos;
header_view = m_view->horizontalHeader();
m_drag_previous_pos = mouse_event->pos().x();
}
//using minimal size = m_sensibility * 2 to prevent collapsing
header_view->resizeSection(m_drag_section,
qMax(m_sensibility * 2, header_view->sectionSize(m_drag_section) + delta));
return true;
} else { // set mouse cursor shape
if (index_resizable(mouse_event->pos(), Qt::Vertical).isValid()) {
m_view->viewport()->setCursor(Qt::SplitVCursor);
} else if (index_resizable(mouse_event->pos(), Qt::Horizontal).isValid()) {
m_view->viewport()->setCursor(Qt::SplitHCursor);
} else {
m_view->viewport()->setCursor(QCursor());
}
}
} else if (mouse_event->type() == QEvent::MouseButtonPress &&
mouse_event->button() == Qt::LeftButton &&
!m_drag_in_progress) { // start dragging
if (index_resizable(mouse_event->pos(), Qt::Vertical).isValid()) {
m_drag_in_progress = true;
m_drag_orientation = Qt::Vertical;
m_drag_previous_pos = mouse_event->y();
m_drag_section = index_resizable(mouse_event->pos(), Qt::Vertical).row();
return true;
} else if (index_resizable(mouse_event->pos(), Qt::Horizontal).isValid()) {
m_drag_in_progress = true;
m_drag_orientation = Qt::Horizontal;
m_drag_previous_pos = mouse_event->x();
m_drag_section = index_resizable(mouse_event->pos(), Qt::Horizontal).column();
return true;
}
} else if (mouse_event->type() == QEvent::MouseButtonRelease &&
mouse_event->button() == Qt::LeftButton &&
m_drag_in_progress) { // stop dragging
m_drag_in_progress = false;
return true;
}
}
}
return false;
}
QModelIndex Table_cell_resizer::index_resizable(QPoint mouse_pos, Qt::Orientation orientation) {
QModelIndex index = m_view->indexAt(mouse_pos - QPoint(m_sensibility + 1, m_sensibility + 1));
if (index.isValid()) {
if (orientation == Qt::Horizontal) {
if (qAbs(m_view->visualRect(index).right() - mouse_pos.x()) < m_sensibility &&
m_view->horizontalHeader()->sectionResizeMode(index.column()) == QHeaderView::Interactive) {
return index;
}
} else {
if (qAbs(m_view->visualRect(index).bottom() - mouse_pos.y()) < m_sensibility &&
m_view->verticalHeader()->sectionResizeMode(index.row()) == QHeaderView::Interactive) {
return index;
}
}
}
return QModelIndex();
}
Usage:
new Table_cell_resizer(ui->table);
User can now resize rows and columns using cell area in addition to header areas. You can hide headers if you wish. This implementation respects header resize modes, so make sure that resize mode is set to QHeaderView::Interactive for headers which you want to be resizable. For example, you can set horizontal header mode to Interactive and vertical header mode to Fixed, resulting in resizable columns and fixed rows.
I have a QDoubleSpinBox in range 0-7000, but want the value always displayed as 4 digits
(0-> 0000, 1 -> 0001 , 30 -> 0030, 3333 -> 3333).
I understand I can add a prefix, but a prefix is always added.
What are my options?
If you use integers, then QSpinBox will be enough.
You can simply inherit from QSpinBox and re-implement the textFromValue function:
class MySpinBox: public QSpinBox
{
Q_OBJECT
public:
MySpinBox( QWidget * parent = 0) :
QSpinBox(parent)
{
}
virtual QString textFromValue ( int value ) const
{
/* 4 - number of digits, 10 - base of number, '0' - pad character*/
return QString("%1").arg(value, 4 , 10, QChar('0'));
}
};
Filling QString this way does the trick.
Since prefix is not an option solution if you consider negative values, in my opinion the best and most elegant solution is defining your own custom spin box by deriving QAbstractSpinBox. Here is a small example:
Note that it is far from perfection and it serves just as an example on what could be done:
q4digitspinbox.h:
#ifndef Q4DIGITSPINBOX_H
#define Q4DIGITSPINBOX_H
#include <QAbstractSpinBox>
#include <QLineEdit>
class Q4DigitSpinBox : public QAbstractSpinBox
{
Q_OBJECT
public:
explicit Q4DigitSpinBox(QWidget *parent = 0);
StepEnabled stepEnabled() const;
double maximum() const;
double minimum() const;
void setMaximum(double max);
void setMinimum(double min);
void setRange(double minimum, double maximum);
double value() const;
public slots:
virtual void stepBy(int steps);
void setValue(double val);
signals:
void valueChanged(double i);
void valueChanged(const QString & text);
private:
double m_value;
double m_minimum;
double m_maximum;
QLineEdit m_lineEdit;
};
#endif // Q4DIGITSPINBOX_H
q4digitspinbox.h:
#include "q4digitspinbox.h"
Q4DigitSpinBox::Q4DigitSpinBox(QWidget *parent) :
QAbstractSpinBox(parent),
m_value(0),
m_minimum(-99),
m_maximum(99)
{
setLineEdit(&m_lineEdit);
setValue(0.0);
}
QAbstractSpinBox::StepEnabled Q4DigitSpinBox::stepEnabled() const
{
return StepUpEnabled | StepDownEnabled;
}
double Q4DigitSpinBox::maximum() const
{
return m_maximum;
}
double Q4DigitSpinBox::minimum() const
{
return m_minimum;
}
void Q4DigitSpinBox::setMaximum(double max)
{
m_maximum = max;
}
void Q4DigitSpinBox::setMinimum(double min)
{
m_minimum = min;
}
void Q4DigitSpinBox::setRange(double minimum, double maximum)
{
m_minimum = minimum;
m_maximum = maximum;
}
double Q4DigitSpinBox::value() const
{
return m_value;
}
void Q4DigitSpinBox::stepBy(int steps)
{
m_value += (double)steps / 10;
if (fabs(m_value - 0) < 0.00001)
{
m_value = 0;
}
if(m_value < m_minimum || m_value > m_maximum)
{
return;
}
int prefixNumberOfDigits = 4;
QString valueAsString = QString("%1").arg((int)m_value);
int numberOfDigits = valueAsString.length();
QString prefix;
prefixNumberOfDigits -= numberOfDigits;
if(prefixNumberOfDigits > 0)
{
while(prefixNumberOfDigits--)
{
prefix += "0";
}
}
QString value;
if(m_value < 0)
{
value = QString("-%1%2").arg(prefix).arg(-m_value);
}
else
{
value = QString("%1%2").arg(prefix).arg(m_value);
}
m_lineEdit.setText(value);
emit valueChanged(m_value);
emit valueChanged(value);
}
void Q4DigitSpinBox::setValue(double val)
{
if(val < m_minimum || val > m_maximum)
{
return;
}
int prefixNumberOfDigits = 4;
QString valueAsString = QString("%1").arg((int)val);
int numberOfDigits = valueAsString.length();
QString prefix;
prefixNumberOfDigits -= numberOfDigits;
if(prefixNumberOfDigits > 0)
{
while(prefixNumberOfDigits--)
{
prefix += "0";
}
}
QString value;
if(val < 0)
{
value = QString("-%1%2").arg(prefix).arg(-val);
}
else
{
value = QString("%1%2").arg(prefix).arg(val);
}
m_lineEdit.setText(value);
emit valueChanged(val);
emit valueChanged(value);
}
I didn't provide any commentary since I considered it pretty straight forward, but if needed I can add a few more explanations.
I hope this helps.