partial line drawing for smith/polar charts

This commit is contained in:
Jan Käberich 2022-08-21 13:02:29 +02:00
parent 0f22bd1287
commit 75ebed2b5d
4 changed files with 94 additions and 16 deletions

View File

@ -13,6 +13,7 @@ TracePolar::TracePolar(TraceModel &model, QWidget *parent)
limitToSpan = true;
limitToEdge = true;
edgeReflection = 1.0;
dx = 0;
initializeTraceInfo();
}
@ -77,7 +78,7 @@ QPoint TracePolar::dataToPixel(std::complex<double> d)
return transform.map(QPoint(d.real() * polarCoordMax * (1.0 / edgeReflection), -d.imag() * polarCoordMax * (1.0 / edgeReflection)));
}
QPoint TracePolar::dataToPixel(Trace::Data d)
QPoint TracePolar:: dataToPixel(Trace::Data d)
{
return dataToPixel(d.y);
}
@ -236,6 +237,71 @@ void TracePolar::updateContextMenu()
finishContextMenu();
}
bool TracePolar::constrainLineToCircle(QPointF &a, QPointF &b, QPointF center, double radius)
{
auto distance = [](const QPointF &a, const QPointF &b) {
auto dx = b.x() - a.x();
auto dy = b.y() - a.y();
return sqrt(dx*dx + dy*dy);
};
if(distance(a, center) <= radius && distance(b, center) <= radius) {
// both points are completely contained within the circle, no adjustment necessary
return true;
}
// shift points, the formulas assume center = (0,0)
a -= center;
b -= center;
// according to https://mathworld.wolfram.com/Circle-LineIntersection.html
auto dx = b.x() - a.x();
auto dy = b.y() - a.y();
auto dr = sqrt(dx*dx+dy*dy);
auto D = a.x()*b.y() - b.x()*a.y();
// check intersection
auto delta = radius*radius * dr*dr - D*D;
if(delta <= 0) {
// line does not intersect the circle
return false;
}
// line intersects the circle, calculate intersection points
auto x1 = (D*dy+copysign(1.0, dy) * dx*sqrt(delta)) / (dr*dr);
auto x2 = (D*dy-copysign(1.0, dy) * dx*sqrt(delta)) / (dr*dr);
auto y1 = (-D*dx+abs(dy)*sqrt(delta)) / (dr*dr);
auto y2 = (-D*dx-abs(dy)*sqrt(delta)) / (dr*dr);
auto inter1 = QPointF(x1, y1);
auto inter2 = QPointF(x2, y2);
bool inter1betweenPoints = false;
bool inter2betweenPoints = false;
if(abs(distance(a, inter1) + distance(b, inter1) - distance(a, b)) < 0.000001) {
inter1betweenPoints = true;
}
if(abs(distance(a, inter2) + distance(b, inter2) - distance(a, b)) < 0.000001) {
inter2betweenPoints = true;
}
if(inter1betweenPoints && inter2betweenPoints) {
// adjust both points, order does not matter
a = inter1;
b = inter2;
} else {
// exactly one intersection point must lie between the two line points, otherwise we would have returned already
auto inter = inter1betweenPoints ? inter1 : inter2;
if(distance(a, QPointF(0,0)) < radius) {
// point is in the circle and can remain unchanged. Use inter as new point b
b = inter;
} else {
// the other way around
a = inter;
}
}
a += center;
b += center;
return true;
}
PolarArc::PolarArc(QPointF center, double radius, double startAngle, double spanAngle)
: center(center),
radius(radius),

View File

@ -43,6 +43,11 @@ protected:
virtual void updateContextMenu() override;
virtual bool supported(Trace *t) override {Q_UNUSED(t) return false;};
// given two points and a circle, the two points are adjusted in such a way that the line they describe
// is constrained within the circle. Returns true if there is a remaining line segment in the circle, false
// if the line lies completely outside of the circle (or is tangent to the circle)
static bool constrainLineToCircle(QPointF &a, QPointF &b, QPointF center, double radius);
bool limitToSpan;
bool limitToEdge;
double edgeReflection; // magnitude of reflection coefficient at the edge of the polar chart (zoom factor)

View File

@ -157,13 +157,18 @@ void TracePolarChart::draw(QPainter &p) {
last = dataAddDx(last);
now = dataAddDx(now);
if (limitToEdge && (abs(last.y) > edgeReflection || abs(now.y) > edgeReflection)) {
// outside of visible area
continue;
// scale to size of smith diagram
QPointF p1 = dataToPixel(last);
QPointF p2 = dataToPixel(now);
if(limitToEdge && (abs(last.y) > edgeReflection || abs(now.y) > edgeReflection)) {
// partially outside of visible area, constrain
if(!TracePolar::constrainLineToCircle(p1, p2, transform.map(QPointF(0,0)), polarCoordMax * scale)) {
// completely out of visible area
continue;
}
}
// scale to size of diagram
auto p1 = dataToPixel(last);
auto p2 = dataToPixel(now);
// draw line
p.drawLine(p1, p2);
}
@ -215,9 +220,6 @@ void TracePolarChart::draw(QPainter &p) {
bool TracePolarChart::dropSupported(Trace *t)
{
if(!t->isReflection()) {
return false;
}
switch(t->outputType()) {
case Trace::DataType::Frequency:
return true;

View File

@ -247,13 +247,18 @@ void TraceSmithChart::draw(QPainter &p) {
last = dataAddDx(last);
now = dataAddDx(now);
if (limitToEdge && (abs(last.y) > edgeReflection || abs(now.y) > edgeReflection)) {
// outside of visible area
continue;
}
// scale to size of smith diagram
auto p1 = dataToPixel(last);
auto p2 = dataToPixel(now);
QPointF p1 = dataToPixel(last);
QPointF p2 = dataToPixel(now);
if(limitToEdge && (abs(last.y) > edgeReflection || abs(now.y) > edgeReflection)) {
// partially outside of visible area, constrain
if(!TracePolar::constrainLineToCircle(p1, p2, transform.map(QPointF(0,0)), polarCoordMax * scale)) {
// completely out of visible area
continue;
}
}
// draw line
p.drawLine(p1, p2);
}