Skip to content

Commit d69b69c

Browse files
imp: add canvas rotation
closes #417
1 parent 5780aff commit d69b69c

25 files changed

+365
-64
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ SRC_IMP = \
480480
src/util/zmq_helper.cpp\
481481
src/widgets/help_button.cpp\
482482
src/util/keep_slope_util.cpp\
483+
src/imp/view_angle_window.cpp\
483484

484485
SRC_IMPC = \
485486
3rd_party/footag/wiz.c\

src/canvas/canvas.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ class Canvas {
7171
return false;
7272
};
7373

74+
virtual float get_view_angle() const
75+
{
76+
return 0;
77+
}
78+
7479
std::pair<Coordf, Coordf> get_bbox(bool visible_only = true) const;
7580

7681
static const int first_overlay_layer = 30000;

src/canvas/canvas_gl.cpp

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -414,29 +414,30 @@ void CanvasGL::request_push(PushFilter filter)
414414
queue_draw();
415415
}
416416

417-
void CanvasGL::center_and_zoom(const Coordi &center, float sc)
417+
void CanvasGL::center_and_zoom(const Coordf &center, float sc)
418418
{
419419
// we want center to be at width, height/2
420420
if (sc > 0)
421421
scale = sc;
422-
offset.x = -((center.x * (flip_view ? -1 : 1) * scale) - m_width / 2);
423-
offset.y = -((center.y * -scale) - m_height / 2);
422+
const Coordf m(m_width / 2, m_height / 2);
423+
const auto c = canvas2screen(center);
424+
offset -= (c - m);
424425
update_viewmat();
425426
s_signal_scale_changed.emit();
426427
queue_draw();
427428
}
428429

429-
void CanvasGL::zoom_to_bbox(const Coordf &a, const Coordf &b)
430+
void CanvasGL::zoom_to_bbox(const Coordf &a_a, const Coordf &a_b)
430431
{
432+
Placement tr;
433+
tr.set_angle_rad(view_angle);
434+
auto [a, b] = tr.transform_bb(std::make_pair(a_a, a_b));
431435
auto sc_x = m_width / abs(a.x - b.x);
432436
auto sc_y = m_height / abs(a.y - b.y);
433437
scale = std::min(sc_x, sc_y);
434-
auto center = (a + b) / 2;
435-
offset.x = -((center.x * (flip_view ? -1 : 1) * scale) - m_width / 2);
436-
offset.y = -((center.y * -scale) - m_height / 2);
438+
auto center = (a_a + a_b) / 2;
437439
update_viewmat();
438-
s_signal_scale_changed.emit();
439-
queue_draw();
440+
center_and_zoom(center, scale);
440441
}
441442

442443
void CanvasGL::zoom_to_bbox(const std::pair<Coordf, Coordf> &bb)
@@ -467,7 +468,8 @@ void CanvasGL::update_viewmat()
467468
auto scale_x = scale;
468469
if (flip_view)
469470
scale_x = -scale;
470-
viewmat = glm::scale(glm::translate(glm::mat3(1), glm::vec2(offset.x, offset.y)), glm::vec2(scale_x, -scale));
471+
viewmat = glm::scale(glm::rotate(glm::translate(glm::mat3(1), glm::vec2(offset.x, offset.y)), -view_angle),
472+
glm::vec2(scale_x, -scale));
471473
viewmat_noflip = glm::scale(glm::translate(glm::mat3(1), glm::vec2(offset.x, offset.y)), glm::vec2(scale, -scale));
472474
}
473475

@@ -499,22 +501,51 @@ void CanvasGL::set_flip_view(bool fl)
499501
auto toggled = fl != flip_view;
500502
flip_view = fl;
501503
if (toggled) {
502-
offset.x = m_width - offset.x;
504+
// mirror offset at vertical (angle 0) line at view angle through viewport center
505+
const Coordf m(m_width / 2, m_height / 2);
506+
const Coordf p = offset;
507+
const Coordf l(sin(view_angle), cos(view_angle));
508+
const auto u = l.dot(p) - l.dot(m);
509+
const auto pc = m + l * u;
510+
const auto pm = pc * 2 - p;
511+
offset = pm;
503512
}
504513
update_viewmat();
505514
}
506515

516+
void CanvasGL::set_view_angle(float angle)
517+
{
518+
const auto delta = angle - view_angle;
519+
const Coordf m(m_width / 2, m_height / 2);
520+
const auto o = offset - m;
521+
const auto o2 = o.rotate(-delta);
522+
offset += (o2 - o);
523+
view_angle = angle;
524+
update_viewmat();
525+
}
526+
507527
bool CanvasGL::get_flip_view() const
508528
{
509529
return flip_view;
510530
}
511531

532+
float CanvasGL::get_view_angle() const
533+
{
534+
return view_angle;
535+
}
536+
512537
Coordf CanvasGL::screen2canvas(const Coordf &p) const
513538
{
514539
auto cp = glm::inverse(viewmat) * glm::vec3(p.x, p.y, 1);
515540
return {cp.x, cp.y};
516541
}
517542

543+
Coordf CanvasGL::canvas2screen(const Coordf &p) const
544+
{
545+
auto cp = viewmat * glm::vec3(p.x, p.y, 1);
546+
return {cp.x, cp.y};
547+
}
548+
518549
std::set<SelectableRef> CanvasGL::get_selection()
519550
{
520551
std::set<SelectableRef> r;

src/canvas/canvas_gl.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ class CanvasGL : public Canvas, public Gtk::GLArea {
6868
void set_flip_view(bool fl);
6969
bool get_flip_view() const override;
7070

71+
void set_view_angle(float a);
72+
float get_view_angle() const override;
73+
7174
void set_cursor_size(float size);
7275
void set_cursor_size(Appearance::CursorSize);
7376

@@ -118,7 +121,7 @@ class CanvasGL : public Canvas, public Gtk::GLArea {
118121
return s_signal_scale_changed;
119122
}
120123

121-
void center_and_zoom(const Coordi &center, float scale = -1);
124+
void center_and_zoom(const Coordf &center, float scale = -1);
122125
void zoom_to_bbox(const Coordf &a, const Coordf &b);
123126
void zoom_to_bbox(const std::pair<Coordf, Coordf> &bb);
124127
void ensure_min_size(float w, float h);
@@ -204,7 +207,9 @@ class CanvasGL : public Canvas, public Gtk::GLArea {
204207
glm::mat3 viewmat;
205208
glm::mat3 viewmat_noflip;
206209
bool flip_view = false;
210+
float view_angle = 0;
207211
void update_viewmat();
212+
Coordf canvas2screen(const Coordf &p) const;
208213

209214
Coord<float> cursor_pos;
210215
Coord<int64_t> cursor_pos_grid;

src/canvas/drag_selection.cpp

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "common/layer_provider.hpp"
55
#include "common/object_descr.hpp"
66
#include "gl_util.hpp"
7+
#include "util/util.hpp"
78
#include <glm/gtc/type_ptr.hpp>
89

910
namespace horizon {
@@ -387,48 +388,66 @@ void DragSelection::drag_end(GdkEventButton *button_event)
387388
}
388389
}
389390

391+
static ClipperLib::IntPoint to_pt(const Coordf &p)
392+
{
393+
return ClipperLib::IntPoint(p.x, p.y);
394+
}
395+
390396
void DragSelection::Box::update()
391397
{
392-
float xmin = std::min(sel_a.x, sel_b.x);
393-
float xmax = std::max(sel_a.x, sel_b.x);
394-
float ymin = std::min(sel_a.y, sel_b.y);
395-
float ymax = std::max(sel_a.y, sel_b.y);
398+
const auto sel_center = (sel_a + sel_b) / 2;
399+
const auto sel_a_screen = ca.canvas2screen(sel_a);
400+
const auto sel_b_screen = ca.canvas2screen(sel_b);
401+
const auto sel_width = std::abs(sel_b_screen.x - sel_a_screen.x) / ca.scale;
402+
const auto sel_height = std::abs(sel_b_screen.y - sel_a_screen.y) / ca.scale;
403+
const auto sel_angle = (ca.flip_view ? -1 : 1) * ca.view_angle;
404+
auto in_box = [sel_center, sel_width, sel_height, sel_angle](Coordf p) {
405+
p -= sel_center;
406+
p = p.rotate(sel_angle);
407+
return std::abs(p.x) < sel_width / 2 && std::abs(p.y) < sel_height / 2;
408+
};
409+
auto sq = ca.selection_qualifier;
410+
411+
if (sq == CanvasGL::SelectionQualifier::AUTO) {
412+
if (sel_a_screen.x < sel_b_screen.x)
413+
sq = CanvasGL::SelectionQualifier::INCLUDE_BOX;
414+
else
415+
sq = CanvasGL::SelectionQualifier::TOUCH_BOX;
416+
}
417+
418+
ClipperLib::Path clbox(4);
419+
if (sq == CanvasGL::SelectionQualifier::TOUCH_BOX) {
420+
const auto sz1 = Coordf(sel_width / 2, sel_height / 2).rotate(-sel_angle);
421+
const auto sz2 = Coordf(sel_width / 2, sel_height / -2).rotate(-sel_angle);
422+
clbox.at(0) = to_pt(sel_center + sz1);
423+
clbox.at(1) = to_pt(sel_center + sz2);
424+
clbox.at(2) = to_pt(sel_center - sz1);
425+
clbox.at(3) = to_pt(sel_center - sz2);
426+
}
427+
396428
unsigned int i = 0;
397429
for (auto &it : ca.selectables.items) {
398430
it.set_flag(Selectable::Flag::PRELIGHT, false);
399431
if (ca.selection_filter.can_select(ca.selectables.items_ref[i])) {
400-
auto sq = ca.selection_qualifier;
401432

402-
if (sq == CanvasGL::SelectionQualifier::AUTO) {
403-
if (sel_a.x < sel_b.x)
404-
sq = CanvasGL::SelectionQualifier::INCLUDE_BOX;
405-
else
406-
sq = CanvasGL::SelectionQualifier::TOUCH_BOX;
407-
}
408433

409434
if (sq == CanvasGL::SelectionQualifier::INCLUDE_ORIGIN) {
410-
if (it.x > xmin && it.x < xmax && it.y > ymin && it.y < ymax) {
435+
if (in_box({it.x, it.y})) {
411436
it.set_flag(Selectable::Flag::PRELIGHT, true);
412437
}
413438
fill = true;
414439
}
415440
else if (sq == CanvasGL::SelectionQualifier::INCLUDE_BOX) {
416441
auto corners = it.get_corners();
417-
if (std::all_of(corners.begin(), corners.end(), [xmin, xmax, ymin, ymax](const auto &a) {
418-
return a.x > xmin && a.x < xmax && a.y > ymin && a.y < ymax;
442+
if (std::all_of(corners.begin(), corners.end(), [in_box](const auto &a) {
443+
return in_box({a.x, a.y});
419444
})) {
420445
it.set_flag(Selectable::Flag::PRELIGHT, true);
421446
}
422447
fill = false;
423448
}
424449
else if (sq == CanvasGL::SelectionQualifier::TOUCH_BOX) {
425450
// possible optimisation: don't use clipper
426-
ClipperLib::Path clbox(4);
427-
clbox.at(0) = ClipperLib::IntPoint(xmin, ymin);
428-
clbox.at(1) = ClipperLib::IntPoint(xmin, ymax);
429-
clbox.at(2) = ClipperLib::IntPoint(xmax, ymax);
430-
clbox.at(3) = ClipperLib::IntPoint(xmax, ymin);
431-
432451
ClipperLib::Path sel(4);
433452
auto corners = it.get_corners();
434453
for (size_t j = 0; j < 4; j++) {

src/canvas/grid.cpp

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "canvas_gl.hpp"
33
#include "gl_util.hpp"
44
#include <glm/gtc/type_ptr.hpp>
5+
#include "util/bbox_accumulator.hpp"
56

67
namespace horizon {
78
Grid::Grid(const CanvasGL &c) : ca(c), spacing(1.25_mm, 1.25_mm), mark_size(5)
@@ -53,15 +54,17 @@ void Grid::realize()
5354
GET_LOC(this, grid_mod);
5455
GET_LOC(this, mark_size);
5556
GET_LOC(this, color);
57+
GET_LOC(this, angle);
5658
}
5759

5860
void Grid::render()
5961
{
6062
glUseProgram(program);
6163
glBindVertexArray(vao);
6264
glUniformMatrix3fv(screenmat_loc, 1, GL_FALSE, glm::value_ptr(ca.screenmat));
63-
glUniformMatrix3fv(viewmat_loc, 1, GL_FALSE, glm::value_ptr(ca.viewmat_noflip));
65+
glUniformMatrix3fv(viewmat_loc, 1, GL_FALSE, glm::value_ptr(ca.viewmat));
6466
glUniform1f(mark_size_loc, mark_size);
67+
glUniform1f(angle_loc, ca.view_angle);
6568
auto color = ca.get_color(ColorP::GRID);
6669
glUniform4f(color_loc, color.r, color.g, color.b, ca.appearance.grid_opacity);
6770

@@ -74,9 +77,17 @@ void Grid::render()
7477
sp_px = sp * ca.scale;
7578
}
7679

80+
BBoxAccumulator<Coordf::type> acc;
81+
for (const auto x : {0.f, ca.m_width}) {
82+
for (const auto y : {0.f, ca.m_height}) {
83+
acc.accumulate(ca.screen2canvas({x, y}));
84+
}
85+
}
86+
const auto [a, b] = acc.get();
87+
7788
Coord<float> grid_0;
78-
grid_0.x = (round(((-ca.offset.x / ca.scale) - origin.x) / sp.x) - 1) * sp.x + origin.x;
79-
grid_0.y = (round(((-(ca.m_height - ca.offset.y) / ca.scale) - origin.y) / sp.y) - 1) * sp.y + origin.y;
89+
grid_0.x = (round((a.x - origin.x) / sp.x) - 1) * sp.x + origin.x;
90+
grid_0.y = (round((a.y - origin.y) / sp.y) - 1) * sp.y + origin.y;
8091

8192
if (mul != newmul) {
8293
mul = newmul;
@@ -89,20 +100,20 @@ void Grid::render()
89100
auto spmin = std::min(sp.x, sp.y);
90101
glLineWidth(1 * ca.get_scale_factor());
91102
if (mark_size > 100) {
92-
glUniform1f(mark_size_loc, ca.m_height * 2);
93-
int n = (ca.m_width / ca.scale) / spmin + 4;
103+
glUniform1f(mark_size_loc, (b.y - a.y) * ca.scale * 2);
104+
int n = (b.x - a.x) / spmin + 4;
94105
glUniform1i(grid_mod_loc, n + 1);
95106
glDrawArraysInstanced(GL_LINES, 0, 2, n);
96107

97-
glUniform1f(mark_size_loc, ca.m_width * 2);
98-
n = (ca.m_height / ca.scale) / spmin + 4;
108+
glUniform1f(mark_size_loc, (b.x - a.x) * ca.scale * 2);
109+
n = (b.y - a.y) / spmin + 4;
99110
glUniform1i(grid_mod_loc, 1);
100111
glDrawArraysInstanced(GL_LINES, 2, 2, n);
101112
}
102113
else {
103-
int mod = ceil((ca.m_width / ca.scale) / spmin) + 2;
114+
int mod = ceil((b.x - a.x) / spmin) + 2;
104115
glUniform1i(grid_mod_loc, mod);
105-
int n = mod * ceil((ca.m_height / ca.scale) / spmin + 2);
116+
int n = mod * ceil((b.y - a.y) / spmin + 2);
106117
glDrawArraysInstanced(GL_LINES, 0, 4, n);
107118
}
108119

@@ -138,6 +149,7 @@ void Grid::render_cursor(Coord<int64_t> &coord)
138149
glUniform2f(grid_size_loc, 0, 0);
139150
glUniform2f(grid_0_loc, coord.x, coord.y);
140151
glUniform1i(grid_mod_loc, 1);
152+
glUniform1f(angle_loc, 0);
141153

142154
auto bgcolor = ca.get_color(ColorP::BACKGROUND);
143155
glUniform4f(color_loc, bgcolor.r, bgcolor.g, bgcolor.b, 1);

src/canvas/grid.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,6 @@ class Grid {
3333
GLuint grid_mod_loc;
3434
GLuint mark_size_loc;
3535
GLuint color_loc;
36+
GLuint angle_loc;
3637
};
3738
} // namespace horizon

src/canvas/render.cpp

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,12 @@ void Canvas::render(const Track &track, bool interactive)
282282
set_lod_size(width);
283283
auto vec = (track.from.get_position() - track.to.get_position());
284284
auto length = sqrt(vec.mag_sq());
285-
Placement p(center);
286-
p.set_angle_rad(atan2(vec.y, vec.x));
285+
Placement p;
286+
p.set_angle_rad(get_view_angle());
287+
if (get_flip_view())
288+
p.invert_angle();
289+
p.accumulate(Placement(center));
290+
p.set_angle_rad(p.get_angle_rad() + atan2(vec.y, vec.x));
287291
if (get_flip_view()) {
288292
p.shift.x *= -1;
289293
p.invert_angle();
@@ -1126,7 +1130,17 @@ void Canvas::render_pad_overlay(const Pad &pad)
11261130
text_layer = get_overlay_layer({BoardLayers::TOP_COPPER, BoardLayers::BOTTOM_COPPER}, true);
11271131
}
11281132

1129-
Placement tr = transform;
1133+
Placement tr;
1134+
tr.set_angle_rad(get_view_angle());
1135+
if (get_flip_view())
1136+
tr.invert_angle();
1137+
{
1138+
Placement tr2 = transform;
1139+
if (tr2.mirror)
1140+
tr2.invert_angle();
1141+
tr2.mirror = false;
1142+
tr.accumulate(tr2);
1143+
}
11301144
if (get_flip_view()) {
11311145
tr.shift.x *= -1;
11321146
tr.invert_angle();
@@ -1295,9 +1309,15 @@ void Canvas::render(const Via &via, bool interactive)
12951309
if (show_text_in_vias && interactive && via.junction->net && via.junction->net->name.size()) {
12961310
auto size = (bb.second.x - bb.first.x) * 1.2;
12971311
set_lod_size(size);
1298-
Placement p(via.junction->position);
1312+
Placement p;
1313+
p.set_angle_rad(get_view_angle());
12991314
if (get_flip_view())
1315+
p.invert_angle();
1316+
p.accumulate(Placement(via.junction->position));
1317+
if (get_flip_view()) {
13001318
p.shift.x *= -1;
1319+
}
1320+
p.set_angle(0);
13011321
const auto ol = get_overlay_layer({BoardLayers::BOTTOM_COPPER, BoardLayers::TOP_COPPER}, true);
13021322
draw_bitmap_text_box(p, size, size, via.junction->net->name, ColorP::TEXT_OVERLAY, ol, TextBoxMode::FULL);
13031323
set_lod_size(-1);

0 commit comments

Comments
 (0)