· 8 years ago · Jul 16, 2018, 05:32 PM
1#include "platform.h" // Should be kept first
2#include "BoardView.h"
3#include "history.h"
4#include "utf8/utf8.h"
5#include "utils.h"
6#include "version.h"
7
8#include <cmath>
9#include <iostream>
10#include <limits.h>
11#include <memory>
12#include <stdio.h>
13#ifdef ENABLE_SDL2
14#ifdef _MSC_VER
15#include <SDL.h>
16#else
17#include <SDL2/SDL.h>
18#endif
19#endif
20
21#include "BRDBoard.h"
22#include "Board.h"
23#include "FileFormats/ASCFile.h"
24#include "FileFormats/BDVFile.h"
25#include "FileFormats/BRD2File.h"
26#include "FileFormats/BRDFile.h"
27#include "FileFormats/BVRFile.h"
28#include "FileFormats/CSTFile.h"
29#include "FileFormats/FZFile.h"
30#include "annotations.h"
31#include "imgui/imgui.h"
32
33#include "NetList.h"
34#include "PartList.h"
35#include "vectorhulls.h"
36
37using namespace std;
38using namespace std::placeholders;
39
40#if _MSC_VER
41#define stricmp _stricmp
42#endif
43
44#ifndef _WIN32
45#define stricmp strcasecmp
46#endif
47
48BoardView::~BoardView() {
49 if (m_validBoard) {
50 for (auto &p : m_board->Components()) {
51 if (p->hull) free(p->hull);
52 }
53 m_board->Nets().clear();
54 m_board->Pins().clear();
55 m_board->Components().clear();
56 m_board->OutlinePoints().clear();
57 delete m_file;
58 delete m_board;
59 m_annotations.Close();
60 m_validBoard = false;
61 }
62}
63uint32_t BoardView::byte4swap(uint32_t x) {
64 /*
65 * used to convert RGBA -> ABGR etc
66 */
67 return (((x & 0x000000ff) << 24) | ((x & 0x0000ff00) << 8) | ((x & 0x00ff0000) >> 8) | ((x & 0xff000000) >> 24));
68}
69void BoardView::ThemeSetStyle(const char *name) {
70 ImGuiStyle &style = ImGui::GetStyle();
71
72 if (strcmp(name, "dark") == 0) {
73 style.Colors[ImGuiCol_Text] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
74 style.Colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
75 style.Colors[ImGuiCol_WindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.70f);
76 style.Colors[ImGuiCol_ChildWindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
77 style.Colors[ImGuiCol_PopupBg] = ImVec4(0.05f, 0.05f, 0.10f, 0.90f);
78 style.Colors[ImGuiCol_Border] = ImVec4(0.70f, 0.70f, 0.70f, 0.65f);
79 style.Colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
80 style.Colors[ImGuiCol_FrameBg] =
81 ImVec4(0.30f, 0.30f, 0.30f, 1.0f); // Background of checkbox, radio button, plot, slider, text input
82 style.Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.90f, 0.80f, 0.80f, 0.40f);
83 style.Colors[ImGuiCol_FrameBgActive] = ImVec4(0.90f, 0.65f, 0.65f, 0.45f);
84 style.Colors[ImGuiCol_TitleBg] = ImVec4(0.27f, 0.27f, 0.54f, 0.83f);
85 style.Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.40f, 0.40f, 0.80f, 0.20f);
86 style.Colors[ImGuiCol_TitleBgActive] = ImVec4(0.32f, 0.32f, 0.63f, 0.87f);
87 style.Colors[ImGuiCol_MenuBarBg] = ImVec4(0.40f, 0.40f, 0.55f, 0.80f);
88 style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.20f, 0.25f, 0.30f, 0.60f);
89 style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.40f, 0.40f, 0.80f, 0.30f);
90 style.Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.80f, 0.40f);
91 style.Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 0.40f);
92 style.Colors[ImGuiCol_ComboBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.99f);
93 style.Colors[ImGuiCol_CheckMark] = ImVec4(0.90f, 0.90f, 0.90f, 0.50f);
94 style.Colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
95 style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f);
96 style.Colors[ImGuiCol_Button] = ImVec4(0.67f, 0.40f, 0.40f, 0.60f);
97 style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.67f, 0.40f, 0.40f, 1.00f);
98 style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f);
99 style.Colors[ImGuiCol_Header] = ImVec4(0.40f, 0.40f, 0.90f, 0.45f);
100 style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.45f, 0.45f, 0.90f, 0.80f);
101 style.Colors[ImGuiCol_HeaderActive] = ImVec4(0.53f, 0.53f, 0.87f, 0.80f);
102 style.Colors[ImGuiCol_Column] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
103 style.Colors[ImGuiCol_ColumnHovered] = ImVec4(0.70f, 0.60f, 0.60f, 1.00f);
104 style.Colors[ImGuiCol_ColumnActive] = ImVec4(0.90f, 0.70f, 0.70f, 1.00f);
105 style.Colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
106 style.Colors[ImGuiCol_ResizeGripHovered] = ImVec4(1.00f, 1.00f, 1.00f, 0.60f);
107 style.Colors[ImGuiCol_ResizeGripActive] = ImVec4(1.00f, 1.00f, 1.00f, 0.90f);
108 style.Colors[ImGuiCol_CloseButton] = ImVec4(0.50f, 0.50f, 0.90f, 0.50f);
109 style.Colors[ImGuiCol_CloseButtonHovered] = ImVec4(0.70f, 0.70f, 0.90f, 0.60f);
110 style.Colors[ImGuiCol_CloseButtonActive] = ImVec4(0.70f, 0.70f, 0.70f, 1.00f);
111 style.Colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
112 style.Colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
113 style.Colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
114 style.Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
115 style.Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
116 style.Colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
117
118 m_colors.backgroundColor = byte4swap(0x000000ff);
119 m_colors.boardFillColor = byte4swap(0x2a2a2aff);
120 m_colors.boardOutlineColor = byte4swap(0xcc2222ff);
121 m_colors.partHullColor = byte4swap(0x80808080);
122 m_colors.partOutlineColor = byte4swap(0x999999ff);
123 m_colors.partFillColor = byte4swap(0x111111ff);
124 m_colors.partHighlightedColor = byte4swap(0xffffffff);
125 m_colors.partHighlightedFillColor = byte4swap(0x333333ff);
126 m_colors.partTextColor = byte4swap(0x000000ff);
127 m_colors.partTextBackgroundColor = byte4swap(0xcccc22ff);
128 m_colors.pinDefaultColor = byte4swap(0x4040ffff);
129 m_colors.pinDefaultTextColor = byte4swap(0xccccccff);
130 m_colors.pinGroundColor = byte4swap(0x0300C3ff);
131 m_colors.pinNotConnectedColor = byte4swap(0xaaaaaaff);
132 m_colors.pinTestPadColor = byte4swap(0x888888ff);
133 m_colors.pinTestPadFillColor = byte4swap(0x6c5b1fff);
134
135 m_colors.pinSelectedColor = byte4swap(0x00ff00ff);
136 m_colors.pinSelectedFillColor = byte4swap(0x8888ffff);
137 m_colors.pinSelectedTextColor = byte4swap(0xffffffff);
138
139 m_colors.pinSameNetColor = byte4swap(0x0000ffff);
140 m_colors.pinSameNetFillColor = byte4swap(0x9999ffff);
141 m_colors.pinSameNetTextColor = byte4swap(0x111111ff);
142
143 m_colors.pinHaloColor = byte4swap(0xffffff88);
144 m_colors.pinNetWebColor = byte4swap(0xff888888);
145 m_colors.pinNetWebOSColor = byte4swap(0x8888ff88);
146
147 m_colors.annotationPartAliasColor = byte4swap(0xffff00ff);
148 m_colors.annotationBoxColor = byte4swap(0xcccc88ff);
149 m_colors.annotationStalkColor = byte4swap(0xaaaaaaff);
150 m_colors.annotationPopupBackgroundColor = byte4swap(0x888888ff);
151 m_colors.annotationPopupTextColor = byte4swap(0xffffffff);
152
153 m_colors.selectedMaskPins = byte4swap(0xffffffff);
154 m_colors.selectedMaskParts = byte4swap(0xffffffff);
155 m_colors.selectedMaskOutline = byte4swap(0xffffffff);
156
157 m_colors.orMaskPins = byte4swap(0x0);
158 m_colors.orMaskParts = byte4swap(0x0);
159 m_colors.orMaskOutline = byte4swap(0x0);
160
161 } else {
162 // light theme default
163 style.Colors[ImGuiCol_Text] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
164 style.Colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
165 style.Colors[ImGuiCol_PopupBg] = ImVec4(0.94f, 0.94f, 0.94f, 1.00f);
166 style.Colors[ImGuiCol_WindowBg] = ImVec4(0.94f, 0.94f, 0.94f, 1.00f);
167 style.Colors[ImGuiCol_ChildWindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
168 style.Colors[ImGuiCol_Border] = ImVec4(0.00f, 0.00f, 0.00f, 0.39f);
169 style.Colors[ImGuiCol_BorderShadow] = ImVec4(1.00f, 1.00f, 1.00f, 0.10f);
170 style.Colors[ImGuiCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
171 style.Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);
172 style.Colors[ImGuiCol_FrameBgActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
173 style.Colors[ImGuiCol_TitleBg] = ImVec4(0.96f, 0.96f, 0.96f, 1.00f);
174 style.Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(1.00f, 1.00f, 1.00f, 0.51f);
175 style.Colors[ImGuiCol_TitleBgActive] = ImVec4(0.82f, 0.82f, 0.82f, 1.00f);
176 style.Colors[ImGuiCol_MenuBarBg] = ImVec4(0.82f, 0.82f, 0.82f, 1.00f);
177 style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.98f, 0.98f, 0.98f, 0.53f);
178 style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.69f, 0.69f, 0.69f, 0.80f);
179 style.Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.49f, 0.49f, 0.49f, 0.80f);
180 style.Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.49f, 0.49f, 0.49f, 1.00f);
181 style.Colors[ImGuiCol_ComboBg] = ImVec4(0.86f, 0.86f, 0.86f, 0.99f);
182 style.Colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
183 style.Colors[ImGuiCol_SliderGrab] = ImVec4(0.26f, 0.59f, 0.98f, 0.78f);
184 style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
185 style.Colors[ImGuiCol_Button] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);
186 style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
187 style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.06f, 0.53f, 0.98f, 1.00f);
188 style.Colors[ImGuiCol_Header] = ImVec4(0.26f, 0.59f, 0.98f, 0.31f);
189 style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f);
190 style.Colors[ImGuiCol_HeaderActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
191 style.Colors[ImGuiCol_Column] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);
192 style.Colors[ImGuiCol_ColumnHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.78f);
193 style.Colors[ImGuiCol_ColumnActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
194 style.Colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.00f);
195 style.Colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
196 style.Colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
197 style.Colors[ImGuiCol_CloseButton] = ImVec4(0.59f, 0.59f, 0.59f, 0.50f);
198 style.Colors[ImGuiCol_CloseButtonHovered] = ImVec4(0.98f, 0.39f, 0.36f, 1.00f);
199 style.Colors[ImGuiCol_CloseButtonActive] = ImVec4(0.98f, 0.39f, 0.36f, 1.00f);
200 style.Colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);
201 style.Colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
202 style.Colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
203 style.Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
204 style.Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
205 style.Colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
206
207 m_colors.backgroundColor = byte4swap(0xffffffff);
208 m_colors.boardFillColor = byte4swap(0xddddddff);
209 m_colors.partHullColor = byte4swap(0x80808080);
210 m_colors.partOutlineColor = byte4swap(0x444444ff);
211 m_colors.partFillColor = byte4swap(0xffffffbb);
212 m_colors.partHighlightedColor = byte4swap(0xff0000ff);
213 m_colors.partHighlightedFillColor = byte4swap(0xf0f0f0ff);
214 m_colors.partTextColor = byte4swap(0xff3030ff);
215 m_colors.partTextBackgroundColor = byte4swap(0xffff00ff);
216 m_colors.boardOutlineColor = byte4swap(0x444444ff);
217 m_colors.pinDefaultColor = byte4swap(0x8888ffff);
218 m_colors.pinDefaultTextColor = byte4swap(0x666688ff);
219 m_colors.pinGroundColor = byte4swap(0x2222aaff);
220 m_colors.pinNotConnectedColor = byte4swap(0xaaaaaaff);
221 m_colors.pinTestPadColor = byte4swap(0x888888ff);
222 m_colors.pinTestPadFillColor = byte4swap(0xd6c68dff);
223
224 m_colors.pinSelectedColor = byte4swap(0x00000000);
225 m_colors.pinSelectedFillColor = byte4swap(0x8888ffff);
226 m_colors.pinSelectedTextColor = byte4swap(0xffffffff);
227
228 m_colors.pinSameNetColor = byte4swap(0x888888ff);
229 m_colors.pinSameNetFillColor = byte4swap(0x9999ffff);
230 m_colors.pinSameNetTextColor = byte4swap(0x111111ff);
231
232 m_colors.pinHaloColor = byte4swap(0x22FF2288);
233 m_colors.pinNetWebColor = byte4swap(0xff000044);
234 m_colors.pinNetWebOSColor = byte4swap(0x0000ff33);
235
236 m_colors.annotationPartAliasColor = byte4swap(0xffff00ff);
237 m_colors.annotationBoxColor = byte4swap(0xff0000aa);
238 m_colors.annotationStalkColor = byte4swap(0x000000ff);
239 m_colors.annotationPopupBackgroundColor = byte4swap(0xeeeeeeff);
240 m_colors.annotationPopupTextColor = byte4swap(0x000000ff);
241
242 m_colors.selectedMaskPins = byte4swap(0xffffffff);
243 m_colors.selectedMaskParts = byte4swap(0xffffffff);
244 m_colors.selectedMaskOutline = byte4swap(0xffffffff);
245
246 m_colors.orMaskPins = byte4swap(0x0);
247 m_colors.orMaskParts = byte4swap(0x0);
248 m_colors.orMaskOutline = byte4swap(0x0);
249 }
250}
251
252int BoardView::ConfigParse(void) {
253 ImGuiStyle &style = ImGui::GetStyle();
254 const char *v = obvconfig.ParseStr("colorTheme", "light");
255 ThemeSetStyle(v);
256
257 fontSize = obvconfig.ParseDouble("fontSize", 20);
258 pinSizeThresholdLow = obvconfig.ParseDouble("pinSizeThresholdLow", 0);
259 pinShapeSquare = obvconfig.ParseBool("pinShapeSquare", false);
260 pinShapeCircle = obvconfig.ParseBool("pinShapeCircle", true);
261
262 if ((!pinShapeCircle) && (!pinShapeSquare)) {
263 pinShapeSquare = true;
264 }
265
266 // Special test here, in case we've already set the dpi from external
267 // such as command line.
268 if (!dpi) dpi = obvconfig.ParseInt("dpi", 100);
269 if (dpi < 50) dpi = 50;
270 if (dpi > 400) dpi = 400;
271 dpiscale = dpi / 100.0f;
272
273 pinHalo = obvconfig.ParseBool("pinHalo", true);
274 pinHaloDiameter = obvconfig.ParseDouble("pinHaloDiameter", 1.25);
275 pinHaloThickness = obvconfig.ParseDouble("pinHaloThickness", 2.0);
276 pinSelectMasks = obvconfig.ParseBool("pinSelectMasks", true);
277
278 showFPS = obvconfig.ParseBool("showFPS", false);
279 showInfoPanel = obvconfig.ParseBool("showInfoPanel", true);
280 infoPanelSelectPartsOnNet = obvconfig.ParseBool("infoPanelSelectPartsOnNet", true);
281 infoPanelCenterZoomNets = obvconfig.ParseBool("infoPanelCenterZoomNets", true);
282 partZoomScaleOutFactor = obvconfig.ParseDouble("partZoomScaleOutFactor", 3.0f);
283
284 m_info_surface.x = obvconfig.ParseInt("infoPanelWidth", 350);
285 showPins = obvconfig.ParseBool("showPins", true);
286 showNetWeb = obvconfig.ParseBool("showNetWeb", true);
287 showAnnotations = obvconfig.ParseBool("showAnnotations", true);
288 fillParts = obvconfig.ParseBool("fillParts", true);
289 m_centerZoomSearchResults = obvconfig.ParseBool("centerZoomSearchResults", true);
290 flipMode = obvconfig.ParseInt("flipMode", 0);
291
292 boardFill = obvconfig.ParseBool("boardFill", true);
293 boardFillSpacing = obvconfig.ParseInt("boardFillSpacing", 3);
294
295 zoomFactor = obvconfig.ParseInt("zoomFactor", 10) / 10.0f;
296 zoomModifier = obvconfig.ParseInt("zoomModifier", 5);
297
298 panFactor = obvconfig.ParseInt("panFactor", 30);
299 panFactor = DPI(panFactor);
300
301 panModifier = obvconfig.ParseInt("panModifier", 5);
302
303 annotationBoxSize = obvconfig.ParseInt("annotationBoxSize", 15);
304 annotationBoxSize = DPI(annotationBoxSize);
305
306 annotationBoxOffset = obvconfig.ParseInt("annotationBoxOffset", 8);
307 annotationBoxOffset = DPI(annotationBoxOffset);
308
309 /*
310 * Some machines (Atom etc) don't have enough CPU/GPU
311 * grunt to cope with the large number of AA'd circles
312 * generated on a large dense board like a Macbook Pro
313 * so we have the lowCPU option which will let people
314 * trade good-looks for better FPS
315 *
316 * If we want slowCPU to take impact from a command line
317 * parameter, we need it to be set to false before we
318 * call this.
319 */
320 slowCPU |= obvconfig.ParseBool("slowCPU", false);
321 if (slowCPU == true) {
322 style.AntiAliasedShapes = false;
323 boardFill = false;
324 fillParts = false;
325 }
326
327 /*
328 * Colours in ImGui can be represented as a 4-byte packed uint32_t as ABGR
329 * but most humans are more accustomed to RBGA, so for the sake of readability
330 * we use the human-readable version but swap the ordering around when
331 * it comes to assigning the actual colour to ImGui.
332 */
333
334 /*
335 * XRayBlue theme
336 */
337
338 m_colors.backgroundColor = byte4swap(obvconfig.ParseHex("backgroundColor", byte4swap(m_colors.backgroundColor)));
339 m_colors.boardFillColor = byte4swap(obvconfig.ParseHex("boardFillColor", byte4swap(m_colors.boardFillColor)));
340
341 m_colors.partHullColor = byte4swap(obvconfig.ParseHex("partHullColor", byte4swap(m_colors.partHullColor)));
342 m_colors.partOutlineColor = byte4swap(obvconfig.ParseHex("partOutlineColor", byte4swap(m_colors.partOutlineColor)));
343 m_colors.partFillColor = byte4swap(obvconfig.ParseHex("partFillColor", byte4swap(m_colors.partFillColor)));
344 m_colors.partHighlightedColor = byte4swap(obvconfig.ParseHex("partHighlightedColor", byte4swap(m_colors.partHighlightedColor)));
345 m_colors.partHighlightedFillColor =
346 byte4swap(obvconfig.ParseHex("partHighlightedFillColor", byte4swap(m_colors.partHighlightedFillColor)));
347 m_colors.partTextColor = byte4swap(obvconfig.ParseHex("partTextColor", byte4swap(m_colors.partTextColor)));
348 m_colors.partTextBackgroundColor =
349 byte4swap(obvconfig.ParseHex("partTextBackgroundColor", byte4swap(m_colors.partTextBackgroundColor)));
350 m_colors.boardOutlineColor = byte4swap(obvconfig.ParseHex("boardOutlineColor", byte4swap(m_colors.boardOutlineColor)));
351 m_colors.pinDefaultColor = byte4swap(obvconfig.ParseHex("pinDefaultColor", byte4swap(m_colors.pinDefaultColor)));
352 m_colors.pinDefaultTextColor = byte4swap(obvconfig.ParseHex("pinDefaultTextColor", byte4swap(m_colors.pinDefaultTextColor)));
353 m_colors.pinGroundColor = byte4swap(obvconfig.ParseHex("pinGroundColor", byte4swap(m_colors.pinGroundColor)));
354 m_colors.pinNotConnectedColor = byte4swap(obvconfig.ParseHex("pinNotConnectedColor", byte4swap(m_colors.pinNotConnectedColor)));
355 m_colors.pinTestPadColor = byte4swap(obvconfig.ParseHex("pinTestPadColor", byte4swap(m_colors.pinTestPadColor)));
356 m_colors.pinTestPadFillColor = byte4swap(obvconfig.ParseHex("pinTestPadFillColor", byte4swap(m_colors.pinTestPadFillColor)));
357
358 m_colors.pinSelectedTextColor = byte4swap(obvconfig.ParseHex("pinSelectedTextColor", byte4swap(m_colors.pinSelectedTextColor)));
359 m_colors.pinSelectedFillColor = byte4swap(obvconfig.ParseHex("pinSelectedFillColor", byte4swap(m_colors.pinSelectedFillColor)));
360 m_colors.pinSelectedColor = byte4swap(obvconfig.ParseHex("pinSelectedColor", byte4swap(m_colors.pinSelectedColor)));
361
362 m_colors.pinSameNetTextColor = byte4swap(obvconfig.ParseHex("pinSameNetTextColor", byte4swap(m_colors.pinSameNetTextColor)));
363 m_colors.pinSameNetFillColor = byte4swap(obvconfig.ParseHex("pinSameNetFillColor", byte4swap(m_colors.pinSameNetFillColor)));
364 m_colors.pinSameNetColor = byte4swap(obvconfig.ParseHex("pinSameNetColor", byte4swap(m_colors.pinSameNetColor)));
365
366 m_colors.pinHaloColor = byte4swap(obvconfig.ParseHex("pinHaloColor", byte4swap(m_colors.pinHaloColor)));
367 m_colors.pinNetWebColor = byte4swap(obvconfig.ParseHex("pinNetWebColor", byte4swap(m_colors.pinNetWebColor)));
368
369 m_colors.annotationPartAliasColor =
370 byte4swap(obvconfig.ParseHex("annotationPartAliasColor", byte4swap(m_colors.annotationPartAliasColor)));
371 m_colors.annotationBoxColor = byte4swap(obvconfig.ParseHex("annotationBoxColor", byte4swap(m_colors.annotationBoxColor)));
372 m_colors.annotationStalkColor = byte4swap(obvconfig.ParseHex("annotationStalkColor", byte4swap(m_colors.annotationStalkColor)));
373 m_colors.annotationPopupBackgroundColor =
374 byte4swap(obvconfig.ParseHex("annotationPopupBackgroundColor", byte4swap(m_colors.annotationPopupBackgroundColor)));
375 m_colors.annotationPopupTextColor =
376 byte4swap(obvconfig.ParseHex("annotationPopupTextColor", byte4swap(m_colors.annotationPopupTextColor)));
377
378 m_colors.selectedMaskPins = byte4swap(obvconfig.ParseHex("selectedMaskPins", byte4swap(m_colors.selectedMaskPins)));
379 m_colors.selectedMaskParts = byte4swap(obvconfig.ParseHex("selectedMaskParts", byte4swap(m_colors.selectedMaskParts)));
380 m_colors.selectedMaskOutline = byte4swap(obvconfig.ParseHex("selectedMaskOutline", byte4swap(m_colors.selectedMaskOutline)));
381
382 m_colors.orMaskPins = byte4swap(obvconfig.ParseHex("orMaskPins", byte4swap(m_colors.orMaskPins)));
383 m_colors.orMaskParts = byte4swap(obvconfig.ParseHex("orMaskParts", byte4swap(m_colors.orMaskParts)));
384 m_colors.orMaskOutline = byte4swap(obvconfig.ParseHex("orMaskOutline", byte4swap(m_colors.orMaskOutline)));
385
386 /*
387 * The asus .fz file formats require a specific key to be decoded.
388 *
389 * This key is supplied in the obv.conf file as a long single line
390 * of comma/space separated 32-bit hex values 0x1234abcd etc.
391 *
392 */
393 SetFZKey(obvconfig.ParseStr("FZKey", ""));
394
395 return 0;
396}
397
398int BoardView::LoadFile(const std::string &filename, bool preserve) {
399 float m_dx_tmp = m_dx;
400 float m_dy_tmp = m_dy;
401 float m_mx_tmp = m_mx;
402 float m_my_tmp = m_my;
403 float m_scale_tmp = m_scale;
404 float m_scale_floor_tmp = m_scale_floor;
405 float m_lastWidth_tmp = m_lastWidth;
406 float m_lastHeight_tmp = m_lastHeight;
407
408 m_lastFileOpenWasInvalid = true;
409 m_validBoard = false;
410 if (!filename.empty()) {
411 // clean up the previous file.
412 if (m_file && m_board) {
413 for (auto &p : m_board->Components()) {
414 if (p->hull) free(p->hull);
415 }
416 m_pinHighlighted.clear();
417 m_partHighlighted.clear();
418 m_annotations.Close();
419 m_board->Nets().clear();
420 m_board->Pins().clear();
421 m_board->Components().clear();
422 m_board->OutlinePoints().clear();
423 // delete m_file;
424 // delete m_board;
425 }
426
427 SetLastFileOpenName(filename);
428 std::vector<char> buffer = file_as_buffer(filename);
429 if (!buffer.empty()) {
430 BRDFile *file = nullptr;
431 // Since it is encrypted we cannot use the below logic. Trust the ext.
432 if (check_fileext(filename, ".fz")) {
433 file = new FZFile(buffer, FZKey);
434 } else if (check_fileext(filename, ".bom") || check_fileext(filename, ".asc"))
435 file = new ASCFile(buffer, filename);
436 else if (check_fileext(filename, ".cst"))
437 file = new CSTFile(buffer);
438 else if (BRDFile::verifyFormat(buffer))
439 file = new BRDFile(buffer);
440 else if (BRD2File::verifyFormat(buffer))
441 file = new BRD2File(buffer);
442 else if (BDVFile::verifyFormat(buffer))
443 file = new BDVFile(buffer);
444 else if (BVRFile::verifyFormat(buffer))
445 file = new BVRFile(buffer);
446
447 if (file && file->valid) {
448 SetFile(file);
449 fhistory.Prepend_save(filename);
450 // used by main to know when to update the window title
451 history_file_has_changed = 1;
452 boardMinMaxDone = false;
453 if(!preserve){
454 m_rotation = 0;
455 m_current_side = 0;
456 }
457 // check to see we don't have a flipped board outline
458 EPCCheck();
459
460 m_annotations.SetFilename(filename);
461 m_annotations.Load();
462
463 /*
464 * Set pins to a known lower size, they get resized
465 * in DrawParts() when the component is analysed
466 */
467 for (auto &p : m_board->Pins()) {
468 //auto p = pin.get();
469 p->diameter = 7;
470 }
471
472 if(!preserve){
473 CenterView();
474 }
475 else{
476 m_dx = m_dx_tmp;
477 m_dy = m_dy_tmp;
478 m_mx = m_mx_tmp;
479 m_my = m_my_tmp;
480 m_scale = m_scale_tmp;
481 m_scale_floor = m_scale_floor_tmp;
482 m_lastWidth = m_lastWidth_tmp;
483 m_lastHeight = m_lastHeight_tmp;
484 }
485 m_lastFileOpenWasInvalid = false;
486 m_validBoard = true;
487
488 } else {
489 m_validBoard = false;
490 delete file;
491 }
492 }
493 } else {
494 return 1;
495 }
496
497 return 0;
498}
499
500void BoardView::SetFZKey(const char *keytext) {
501
502 if (keytext) {
503 int ki;
504 const char *p, *limit;
505 char *ep;
506 ki = 0;
507 p = keytext;
508 limit = keytext + strlen(keytext);
509
510 if ((limit - p) > 440) {
511 /*
512 * we *assume* that the key is correctly formatted in the configuration file
513 * as such it should be like FZKey = 0x12345678, 0xabcd1234, ...
514 *
515 * If your key is incorrectly formatted, or incorrect, it'll cause OBV to
516 * likely crash / segfault (for now).
517 */
518 while (p && (p < limit) && ki < 44) {
519
520 // locate the start of the u32 hex value
521 while ((p < limit) && (*p != '0')) p++;
522
523 // decode the next number, ep will be set to the end of the converted string
524 FZKey[ki] = strtoll(p, &ep, 16);
525
526 ki++;
527 p = ep;
528 }
529 }
530 }
531}
532
533void RA(const char *t, int w) {
534 ImVec2 s = ImGui::CalcTextSize(t);
535 s.x = w - s.x;
536 ImGui::Dummy(s);
537 ImGui::SameLine();
538 ImGui::Text("%s", t);
539}
540
541void u32tof4(uint32_t c, float *f) {
542 f[0] = (c & 0xff) / 0xff;
543 c >>= 8;
544 f[1] = (c & 0xff) / 0xff;
545 c >>= 8;
546 f[2] = (c & 0xff) / 0xff;
547 c >>= 8;
548 f[3] = (c & 0xff) / 0xff;
549}
550void BoardView::ColorPreferencesItem(
551 const char *label, int label_width, const char *butlabel, const char *conflabel, int var_width, uint32_t *c) {
552 char buf[20];
553 snprintf(buf, sizeof(buf), "%08X", byte4swap(*c));
554 RA(label, label_width);
555 ImGui::SameLine();
556 ImGui::ColorButton(ImColor(*c));
557 ImGui::SameLine();
558 ImGui::PushItemWidth(var_width);
559 if (ImGui::InputText(
560 butlabel, buf, sizeof(buf), ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsHexadecimal, nullptr, buf)) {
561 *c = byte4swap(strtoll(buf, NULL, 16));
562 snprintf(buf, sizeof(buf), "0x%08x", byte4swap(*c));
563 obvconfig.WriteStr(conflabel, buf);
564 }
565 ImGui::PopItemWidth();
566}
567
568void BoardView::SaveAllColors(void) {
569
570 obvconfig.WriteHex("backgroundColor", byte4swap(m_colors.backgroundColor));
571 obvconfig.WriteHex("boardFillColor", byte4swap(m_colors.boardFillColor));
572 obvconfig.WriteHex("boardOutlineColor", byte4swap(m_colors.boardOutlineColor));
573 obvconfig.WriteHex("partOutlineColor", byte4swap(m_colors.partOutlineColor));
574 obvconfig.WriteHex("partHullColor", byte4swap(m_colors.partHullColor));
575 obvconfig.WriteHex("partFillColor", byte4swap(m_colors.partFillColor));
576 obvconfig.WriteHex("partTextColor", byte4swap(m_colors.partTextColor));
577 obvconfig.WriteHex("partTextBackgroundColor", byte4swap(m_colors.partTextBackgroundColor));
578 obvconfig.WriteHex("partHighlightedColor", byte4swap(m_colors.partHighlightedColor));
579 obvconfig.WriteHex("partHighlightedFillColor", byte4swap(m_colors.partHighlightedFillColor));
580 obvconfig.WriteHex("pinDefaultColor", byte4swap(m_colors.pinDefaultColor));
581 obvconfig.WriteHex("pinDefaultTextColor", byte4swap(m_colors.pinDefaultTextColor));
582 obvconfig.WriteHex("pinGroundColor", byte4swap(m_colors.pinGroundColor));
583 obvconfig.WriteHex("pinNotConnectedColor", byte4swap(m_colors.pinNotConnectedColor));
584 obvconfig.WriteHex("pinTestPadColor", byte4swap(m_colors.pinTestPadColor));
585 obvconfig.WriteHex("pinTestPadFillColor", byte4swap(m_colors.pinTestPadFillColor));
586 obvconfig.WriteHex("pinSelectedColor", byte4swap(m_colors.pinSelectedColor));
587 obvconfig.WriteHex("pinSelectedTextColor", byte4swap(m_colors.pinSelectedTextColor));
588 obvconfig.WriteHex("pinSelectedFillColor", byte4swap(m_colors.pinSelectedFillColor));
589 obvconfig.WriteHex("pinSameNetColor", byte4swap(m_colors.pinSameNetColor));
590 obvconfig.WriteHex("pinSameNetTextColor", byte4swap(m_colors.pinSameNetTextColor));
591 obvconfig.WriteHex("pinSameNetFillColor", byte4swap(m_colors.pinSameNetFillColor));
592 obvconfig.WriteHex("pinHaloColor", byte4swap(m_colors.pinHaloColor));
593 obvconfig.WriteHex("pinNetWebColor", byte4swap(m_colors.pinNetWebColor));
594 obvconfig.WriteHex("pinNetWebOSColor", byte4swap(m_colors.pinNetWebOSColor));
595 obvconfig.WriteHex("annotationPopupTextColor", byte4swap(m_colors.annotationPopupTextColor));
596 obvconfig.WriteHex("annotationPopupBackgroundColor", byte4swap(m_colors.annotationPopupBackgroundColor));
597 obvconfig.WriteHex("annotationBoxColor", byte4swap(m_colors.annotationBoxColor));
598 obvconfig.WriteHex("annotationStalkColor", byte4swap(m_colors.annotationStalkColor));
599 obvconfig.WriteHex("selectedMaskOutline", byte4swap(m_colors.selectedMaskOutline));
600 obvconfig.WriteHex("selectedMaskParts", byte4swap(m_colors.selectedMaskParts));
601 obvconfig.WriteHex("selectedMaskPins", byte4swap(m_colors.selectedMaskPins));
602 obvconfig.WriteHex("orMaskPins", byte4swap(m_colors.orMaskPins));
603 obvconfig.WriteHex("orMaskParts", byte4swap(m_colors.orMaskParts));
604 obvconfig.WriteHex("orMaskOutline", byte4swap(m_colors.orMaskOutline));
605}
606
607void BoardView::ColorPreferences(void) {
608 bool dummy = true;
609 // ImGui::PushStyleColor(ImGuiCol_PopupBg, ImColor(0xffe0e0e0));
610 ImGui::SetNextWindowPosCenter();
611 if (ImGui::BeginPopupModal("Colour Preferences", &dummy, ImGuiWindowFlags_AlwaysAutoResize)) {
612
613 if (m_showColorPreferences) {
614 m_showColorPreferences = false;
615 m_tooltips_enabled = false;
616 }
617
618 ImGui::Dummy(ImVec2(1, DPI(5)));
619 RA("Base theme", DPI(200));
620 ImGui::SameLine();
621 {
622 int tc = 0;
623 const char *v = obvconfig.ParseStr("colorTheme", "default");
624 if (strcmp(v, "dark") == 0) {
625 tc = 1;
626 }
627 if (ImGui::RadioButton("Light", &tc, 0)) {
628 obvconfig.WriteStr("colorTheme", "light");
629 ThemeSetStyle("light");
630 SaveAllColors();
631 m_needsRedraw = true;
632 }
633 ImGui::SameLine();
634 if (ImGui::RadioButton("Dark", &tc, 1)) {
635 obvconfig.WriteStr("colorTheme", "dark");
636 ThemeSetStyle("dark");
637 SaveAllColors();
638 m_needsRedraw = true;
639 }
640 }
641 ImGui::Dummy(ImVec2(1, DPI(5)));
642
643 ColorPreferencesItem("Background", DPI(200), "##Background", "backgroundColor", DPI(150), &m_colors.backgroundColor);
644 ColorPreferencesItem("Board fill", DPI(200), "##Boardfill", "boardFillColor", DPI(150), &m_colors.boardFillColor);
645 ColorPreferencesItem(
646 "Board outline", DPI(200), "##BoardOutline", "boardOutlineColor", DPI(150), &m_colors.boardOutlineColor);
647
648 ImGui::Dummy(ImVec2(1, DPI(10)));
649 ImGui::Text("Parts");
650 ImGui::Separator();
651 ColorPreferencesItem("Outline", DPI(200), "##PartOutline", "partOutlineColor", DPI(150), &m_colors.partOutlineColor);
652 ColorPreferencesItem("Hull", DPI(200), "##PartHull", "partHullColor", DPI(150), &m_colors.partHullColor);
653 ColorPreferencesItem("Fill", DPI(200), "##PartFill", "partFillColor", DPI(150), &m_colors.partFillColor);
654 ColorPreferencesItem(
655 "Selected", DPI(200), "##PartSelected", "partHighlightedColor", DPI(150), &m_colors.partHighlightedColor);
656 ColorPreferencesItem("Fill (selected)",
657 DPI(200),
658 "##PartFillSelected",
659 "partHighlightedFillColor",
660 DPI(150),
661 &m_colors.partHighlightedFillColor);
662 ColorPreferencesItem("Text", DPI(200), "##PartText", "partTextColor", DPI(150), &m_colors.partTextColor);
663 ColorPreferencesItem("Text background",
664 DPI(200),
665 "##PartTextBackground",
666 "partTextBackgroundColor",
667 DPI(150),
668 &m_colors.partTextBackgroundColor);
669
670 ImGui::Dummy(ImVec2(1, DPI(10)));
671 ImGui::Text("Pins");
672 ImGui::Separator();
673 ColorPreferencesItem("Default", DPI(200), "##PinDefault", "pinDefaultColor", DPI(150), &m_colors.pinDefaultColor);
674 ColorPreferencesItem(
675 "Default text", DPI(200), "##PinDefaultText", "pinDefaultTextColor", DPI(150), &m_colors.pinDefaultTextColor);
676 ColorPreferencesItem("Ground", DPI(200), "##PinGround", "pinGroundColor", DPI(150), &m_colors.pinGroundColor);
677 ColorPreferencesItem("NC", DPI(200), "##PinNC", "pinNotConnectedColor", DPI(150), &m_colors.pinNotConnectedColor);
678 ColorPreferencesItem("Test pad", DPI(200), "##PinTP", "pinTestPadColor", DPI(150), &m_colors.pinTestPadColor);
679 ColorPreferencesItem("Test pad fill", DPI(200), "##PinTPF", "pinTestPadFillColor", DPI(150), &m_colors.pinTestPadFillColor);
680
681 ColorPreferencesItem("Selected", DPI(200), "##PinSelectedColor", "pinSelectedColor", DPI(150), &m_colors.pinSelectedColor);
682 ColorPreferencesItem(
683 "Selected fill", DPI(200), "##PinSelectedFillColor", "pinSelectedFillColor", DPI(150), &m_colors.pinSelectedFillColor);
684 ColorPreferencesItem(
685 "Selected text", DPI(200), "##PinSelectedTextColor", "pinSelectedTextColor", DPI(150), &m_colors.pinSelectedTextColor);
686
687 ColorPreferencesItem("Same Net", DPI(200), "##PinSameNetColor", "pinSameNetColor", DPI(150), &m_colors.pinSameNetColor);
688 ColorPreferencesItem(
689 "SameNet fill", DPI(200), "##PinSameNetFillColor", "pinSameNetFillColor", DPI(150), &m_colors.pinSameNetFillColor);
690 ColorPreferencesItem(
691 "SameNet text", DPI(200), "##PinSameNetTextColor", "pinSameNetTextColor", DPI(150), &m_colors.pinSameNetTextColor);
692
693 ColorPreferencesItem("Halo", DPI(200), "##PinHalo", "pinHaloColor", DPI(150), &m_colors.pinHaloColor);
694 ColorPreferencesItem("Net web strands", DPI(200), "##NetWebStrands", "pinNetWebColor", DPI(150), &m_colors.pinNetWebColor);
695 ColorPreferencesItem(
696 "Net web (otherside)", DPI(200), "##NetWebOSStrands", "pinNetWebOSColor", DPI(150), &m_colors.pinNetWebOSColor);
697
698 ImGui::Dummy(ImVec2(1, DPI(10)));
699 ImGui::Text("Annotations");
700 ImGui::Separator();
701 ColorPreferencesItem("Box", DPI(200), "##AnnBox", "annotationBoxColor", DPI(150), &m_colors.annotationBoxColor);
702 ColorPreferencesItem("Stalk", DPI(200), "##AnnStalk", "annotationStalkColor", DPI(150), &m_colors.annotationStalkColor);
703 ColorPreferencesItem(
704 "Popup text", DPI(200), "##AnnPopupText", "annotationPopupTextColor", DPI(150), &m_colors.annotationPopupTextColor);
705 ColorPreferencesItem("Popup background",
706 DPI(200),
707 "##AnnPopupBackground",
708 "annotationPopupBackgroundColor",
709 DPI(150),
710 &m_colors.annotationPopupBackgroundColor);
711
712 ImGui::Dummy(ImVec2(1, DPI(10)));
713 ImGui::Text("Masks");
714 ImGui::Separator();
715 ColorPreferencesItem("Pins", DPI(200), "##MaskPins", "selectedMaskPins", DPI(150), &m_colors.selectedMaskPins);
716 ColorPreferencesItem("Parts", DPI(200), "##MaskParts", "selectedMaskParts", DPI(150), &m_colors.selectedMaskParts);
717 ColorPreferencesItem("Outline", DPI(200), "##MaskOutline", "selectedMaskOutline", DPI(150), &m_colors.selectedMaskOutline);
718 ColorPreferencesItem("Boost (Pins)", DPI(200), "##BoostPins", "orMaskPins", DPI(150), &m_colors.orMaskPins);
719 ColorPreferencesItem("Boost (Parts)", DPI(200), "##BoostParts", "orMaskParts", DPI(150), &m_colors.orMaskParts);
720 ColorPreferencesItem("Boost (Outline)", DPI(200), "##BoostOutline", "orMaskOutline", DPI(150), &m_colors.orMaskOutline);
721
722 ImGui::Separator();
723
724 if (ImGui::Button("Done") || ImGui::IsKeyPressed(SDLK_ESCAPE)) {
725 m_tooltips_enabled = true;
726 ImGui::CloseCurrentPopup();
727 }
728 ImGui::EndPopup();
729 }
730
731 if (!dummy) {
732 m_tooltips_enabled = true;
733 }
734 // ImGui::PopStyleColor();
735}
736
737void BoardView::Preferences(void) {
738 bool dummy = true;
739 // ImGui::PushStyleColor(ImGuiCol_PopupBg, ImColor(0xffe0e0e0));
740 ImGui::SetNextWindowPosCenter();
741 if (ImGui::BeginPopupModal("Preferences", &dummy, ImGuiWindowFlags_AlwaysAutoResize)) {
742 int t;
743 if (m_showPreferences) {
744 m_showPreferences = false;
745 m_tooltips_enabled = false;
746 }
747
748 t = obvconfig.ParseInt("windowX", 1100);
749 RA("Window Width", DPI(200));
750 ImGui::SameLine();
751 if (ImGui::InputInt("##windowX", &t)) {
752 if (t > 400) obvconfig.WriteInt("windowX", t);
753 }
754
755 t = obvconfig.ParseInt("windowY", 700);
756 RA("Window Height", DPI(200));
757 ImGui::SameLine();
758 if (ImGui::InputInt("##windowY", &t)) {
759 if (t > 320) obvconfig.WriteInt("windowY", t);
760 }
761
762 const char *oldFont = obvconfig.ParseStr("fontName", "");
763 std::vector<char> newFont(oldFont, oldFont + strlen(oldFont) + 1); // Copy string data + '\0' char
764 if (newFont.size() < 256) // reserve space for new font name
765 newFont.resize(256, '\0'); // Max font name length is 255 characters
766 RA("Font name", DPI(200));
767 ImGui::SameLine();
768 if (ImGui::InputText("##fontName", newFont.data(), newFont.size())) {
769 obvconfig.WriteStr("fontName", newFont.data());
770 }
771
772 t = obvconfig.ParseInt("fontSize", 20);
773 RA("Font size", DPI(200));
774 ImGui::SameLine();
775 if (ImGui::InputInt("##fontSize", &t)) {
776 if ((t >= 8) && (t < 60)) obvconfig.WriteInt("fontSize", t);
777 }
778
779 t = obvconfig.ParseInt("dpi", 100);
780 RA("Screen DPI", DPI(200));
781 ImGui::SameLine();
782 if (ImGui::InputInt("##dpi", &t)) {
783 if ((t > 25) && (t < 600)) obvconfig.WriteInt("dpi", t);
784 }
785 ImGui::PushStyleColor(ImGuiCol_Text, ImColor(0xff4040ff));
786 ImGui::Text("(Program restart is required to properly apply font and DPI changes)");
787 ImGui::PopStyleColor();
788
789 ImGui::Separator();
790
791 RA("Board fill spacing", DPI(200));
792 ImGui::SameLine();
793 if (ImGui::InputInt("##boardFillSpacing", &boardFillSpacing)) {
794 obvconfig.WriteInt("boardFillSpacing", boardFillSpacing);
795 }
796
797 t = zoomFactor * 10;
798 RA("Zoom step", DPI(200));
799 ImGui::SameLine();
800 if (ImGui::InputInt("##zoomStep", &t)) {
801 obvconfig.WriteFloat("zoomFactor", t / 10);
802 }
803
804 RA("Zoom modifier", DPI(200));
805 ImGui::SameLine();
806 if (ImGui::InputInt("##zoomModifier", &zoomModifier)) {
807 obvconfig.WriteInt("zoomModifier", zoomModifier);
808 }
809
810 RA("Panning step", DPI(200));
811 ImGui::SameLine();
812 if (ImGui::InputInt("##panningStep", &panFactor)) {
813 obvconfig.WriteInt("panFactor", panFactor);
814 }
815
816 RA("Pan modifier", DPI(200));
817 ImGui::SameLine();
818 if (ImGui::InputInt("##panModifier", &panModifier)) {
819 obvconfig.WriteInt("panModifier", panModifier);
820 }
821
822 ImGui::Dummy(ImVec2(1, DPI(5)));
823 RA("Flip Mode", DPI(200));
824 ImGui::SameLine();
825 {
826 if (ImGui::RadioButton("Viewport", &flipMode, 0)) {
827 obvconfig.WriteInt("flipMode", flipMode);
828 }
829 ImGui::SameLine();
830 if (ImGui::RadioButton("Mouse", &flipMode, 1)) {
831 obvconfig.WriteInt("flipMode", flipMode);
832 }
833 }
834 ImGui::Dummy(ImVec2(1, DPI(5)));
835
836 RA("Annotation flag size", DPI(200));
837 ImGui::SameLine();
838 if (ImGui::InputInt("##annotationBoxSize", &annotationBoxSize)) {
839 obvconfig.WriteInt("annotationBoxSize", annotationBoxSize);
840 }
841
842 RA("Annotation flag offset", DPI(200));
843 ImGui::SameLine();
844 if (ImGui::InputInt("##annotationBoxOffset", &annotationBoxOffset)) {
845 obvconfig.WriteInt("annotationBoxOffset", annotationBoxOffset);
846 }
847 ImGui::Separator();
848
849 if (ImGui::Checkbox("Pin select masks", &pinSelectMasks)) {
850 obvconfig.WriteBool("pinSelectMasks", pinSelectMasks);
851 }
852
853 if (ImGui::Checkbox("Pin Halo", &pinHalo)) {
854 obvconfig.WriteBool("pinHalo", pinHalo);
855 }
856 RA("Halo diameter", DPI(200));
857 ImGui::SameLine();
858 if (ImGui::InputFloat("##haloDiameter", &pinHaloDiameter)) {
859 obvconfig.WriteFloat("pinHaloDiameter", pinHaloDiameter);
860 }
861
862 RA("Halo thickness", DPI(200));
863 ImGui::SameLine();
864 if (ImGui::InputFloat("##haloThickness", &pinHaloThickness)) {
865 obvconfig.WriteFloat("pinHaloThickness", pinHaloThickness);
866 }
867
868 RA("Info Panel Zoom", DPI(200));
869 ImGui::SameLine();
870 if (ImGui::InputFloat("##partZoomScaleOutFactor", &partZoomScaleOutFactor)) {
871 if (partZoomScaleOutFactor < 1.1) partZoomScaleOutFactor = 1.1;
872 obvconfig.WriteFloat("partZoomScaleOutFactor", partZoomScaleOutFactor);
873 }
874
875 if (ImGui::Checkbox("Center/Zoom Search Results", &m_centerZoomSearchResults)) {
876 obvconfig.WriteBool("centerZoomSearchResults", m_centerZoomSearchResults);
877 }
878
879 if (ImGui::Checkbox("Show Info Panel", &showInfoPanel)) {
880 obvconfig.WriteBool("showInfoPanel", showInfoPanel);
881 }
882
883 if (ImGui::Checkbox("Show net web", &showNetWeb)) {
884 obvconfig.WriteBool("showNetWeb", showNetWeb);
885 }
886
887 if (ImGui::Checkbox("slowCPU", &slowCPU)) {
888 obvconfig.WriteBool("slowCPU", slowCPU);
889 }
890
891 ImGui::SameLine();
892 if (ImGui::Checkbox("Show FPS", &showFPS)) {
893 obvconfig.WriteBool("showFPS", showFPS);
894 }
895
896 if (ImGui::Checkbox("Fill Parts", &fillParts)) {
897 obvconfig.WriteBool("fillParts", fillParts);
898 }
899
900 ImGui::SameLine();
901 if (ImGui::Checkbox("Fill Board", &boardFill)) {
902 obvconfig.WriteBool("boardFill", boardFill);
903 }
904
905 ImGui::Separator();
906 {
907 char keybuf[1024];
908 int i;
909 ImGui::Text("FZ Key");
910 ImGui::SameLine();
911 for (i = 0; i < 44; i++) {
912 sprintf(keybuf + (i * 12),
913 "0x%08lx%s",
914 (long unsigned int)FZKey[i],
915 (i != 43) ? ((i + 1) % 4 ? " " : "\r\n")
916 : ""); // yes, a nested inline-if-else. add \r\n after every 4 values, except if the last
917 }
918 if (ImGui::InputTextMultiline(
919 "##fzkey", keybuf, sizeof(keybuf), ImVec2(DPI(450), ImGui::GetTextLineHeight() * 12.5), 0, NULL, keybuf)) {
920
921 // Strip the line breaks out.
922 char *c = keybuf;
923 while (*c) {
924 if ((*c == '\r') || (*c == '\n')) *c = ' ';
925 c++;
926 }
927 obvconfig.WriteStr("FZKey", keybuf);
928 SetFZKey(keybuf);
929 }
930 }
931
932 ImGui::Separator();
933
934 if (ImGui::Button("Done") || ImGui::IsKeyPressed(SDLK_ESCAPE)) {
935 ImGui::CloseCurrentPopup();
936 m_tooltips_enabled = true;
937 }
938 ImGui::EndPopup();
939 }
940
941 if (!dummy) {
942 m_tooltips_enabled = true;
943 }
944 // ImGui::PopStyleColor();
945}
946
947void BoardView::HelpAbout(void) {
948 bool dummy = true;
949 ImGui::SetNextWindowPosCenter();
950 if (ImGui::BeginPopupModal("About", &dummy, ImGuiWindowFlags_AlwaysAutoResize)) {
951 if (m_showHelpAbout) {
952 m_showHelpAbout = false;
953 m_tooltips_enabled = false;
954 }
955 ImGui::Text("%s %s", OBV_NAME, OBV_VERSION);
956 ImGui::Text("Build %s %s", OBV_BUILD, __TIMESTAMP__);
957 ImGui::Text(OBV_URL);
958 if (ImGui::Button("Close") || ImGui::IsKeyPressed(SDLK_ESCAPE)) {
959 m_tooltips_enabled = true;
960 ImGui::CloseCurrentPopup();
961 }
962 ImGui::Dummy(ImVec2(1, DPI(10)));
963 ImGui::Text("License info");
964 ImGui::Separator();
965 ImGui::TextWrapped(OBV_LICENSE_TEXT);
966
967 ImGui::EndPopup();
968 }
969
970 if (!dummy) {
971 m_tooltips_enabled = true;
972 }
973}
974
975void BoardView::HelpControls(void) {
976 bool dummy = true;
977 ImGui::SetNextWindowPosCenter();
978 if (ImGui::BeginPopupModal("Controls", &dummy, ImGuiWindowFlags_AlwaysAutoResize)) {
979 if (m_showHelpControls) {
980 m_showHelpControls = false;
981 m_tooltips_enabled = false;
982 }
983 ImGui::Text("KEYBOARD CONTROLS");
984 ImGui::SameLine();
985
986 if (ImGui::Button("Close") || ImGui::IsKeyPressed(SDLK_ESCAPE)) {
987 m_tooltips_enabled = true;
988 ImGui::CloseCurrentPopup();
989 }
990 ImGui::Separator();
991
992 ImGui::Columns(2);
993 ImGui::PushItemWidth(-1);
994 ImGui::Text("Load file");
995 ImGui::Text("Quit");
996 ImGui::Spacing();
997 ImGui::Spacing();
998
999 ImGui::Text("Pan up");
1000 ImGui::Text("Pan down");
1001 ImGui::Text("Pan left");
1002 ImGui::Text("Pan right");
1003 ImGui::Spacing();
1004 ImGui::Spacing();
1005
1006 ImGui::Text("Toggle information panel");
1007 ImGui::Text("Search for component/Net");
1008 ImGui::Text("Display component list");
1009 ImGui::Text("Display net list");
1010 ImGui::Text("Clear all highlighted items");
1011 ImGui::Spacing();
1012 ImGui::Spacing();
1013
1014 ImGui::Text("Mirror board");
1015 ImGui::Text("Flip board");
1016 ImGui::Text(" ");
1017
1018 ImGui::Text("Zoom in");
1019 ImGui::Text("Zoom out");
1020 ImGui::Spacing();
1021 ImGui::Spacing();
1022
1023 ImGui::Text("Reset zoom and center");
1024 ImGui::Text("Rotate clockwise");
1025 ImGui::Text("Rotate anticlockwise");
1026 ImGui::Spacing();
1027 ImGui::Spacing();
1028
1029 ImGui::Text("Toggle Pin Blanking");
1030
1031 /*
1032 * NEXT COLUMN
1033 */
1034 ImGui::NextColumn();
1035 ImGui::Text("CTRL-o");
1036 ImGui::Text("CTRL-q");
1037 ImGui::Spacing();
1038 ImGui::Spacing();
1039
1040 ImGui::Text("w, numpad-up, arrow-up");
1041 ImGui::Text("s, numpad-down, arrow-down");
1042 ImGui::Text("a, numpad-left, arrow-left");
1043 ImGui::Text("d, numpad-right, arrow-right");
1044 ImGui::Spacing();
1045 ImGui::Spacing();
1046
1047 ImGui::Text("i");
1048 ImGui::Text("CTRL-f, /");
1049 ImGui::Text("k");
1050 ImGui::Text("l");
1051 ImGui::Text("ESC");
1052 ImGui::Spacing();
1053 ImGui::Spacing();
1054
1055 ImGui::Text("m");
1056 ImGui::Text("Space bar");
1057 ImGui::Text("(+shift to hold position)");
1058 ImGui::Spacing();
1059
1060 ImGui::Text("+, numpad+");
1061 ImGui::Text("-, numpad-");
1062 ImGui::Spacing();
1063 ImGui::Spacing();
1064
1065 ImGui::Text("x, numpad-5");
1066 ImGui::Text("'.' numpad '.'");
1067 ImGui::Text("',' numpad-0");
1068 ImGui::Spacing();
1069 ImGui::Spacing();
1070
1071 ImGui::Text("p");
1072
1073 ImGui::Columns(1);
1074 ImGui::Separator();
1075
1076 ImGui::Text("MOUSE CONTROLS");
1077 ImGui::Separator();
1078 ImGui::Columns(2);
1079 ImGui::Text("Highlight pins on network");
1080 ImGui::Text("Move board");
1081 ImGui::Text("Zoom (CTRL for finer steps)");
1082 ImGui::Text("Flip board");
1083 ImGui::Text("Annotations menu");
1084
1085 ImGui::NextColumn();
1086 ImGui::Text("Click (on pin)");
1087 ImGui::Text("Click and drag");
1088 ImGui::Text("Scroll");
1089 ImGui::Text("Middle click");
1090 ImGui::Text("Right click");
1091 ImGui::Columns(1);
1092
1093 ImGui::EndPopup();
1094 }
1095 if (!dummy) {
1096 m_tooltips_enabled = true;
1097 }
1098}
1099
1100void BoardView::ShowInfoPane(void) {
1101 ImGuiIO &io = ImGui::GetIO();
1102 ImVec2 ds = io.DisplaySize;
1103
1104 if (!showInfoPanel) return;
1105
1106 if (m_info_surface.x < DPIF(100)) {
1107 // fprintf(stderr,"Too small (%f), set to (%f)\n", width, DPIF(100));
1108 m_info_surface.x = DPIF(100) + 1;
1109 m_board_surface.x = ds.x - m_info_surface.x;
1110 }
1111
1112 m_info_surface.y = m_board_surface.y;
1113
1114 /*
1115 * Originally the dialog was to follow the cursor but it proved to be an overkill
1116 * to try adjust things to keep it within the bounds of the window so as to not
1117 * lose the buttons.
1118 *
1119 * Now it's kept at a fixed point.
1120 */
1121 ImGui::SetNextWindowPos(ImVec2(ds.x - m_info_surface.x, m_menu_height));
1122 ImGui::SetNextWindowSize(m_info_surface);
1123 ImGui::Begin("Info Panel",
1124 NULL,
1125 ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize |
1126 ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_NoSavedSettings);
1127
1128 if ((m_dragging_token == 0) && (io.MousePos.x > m_board_surface.x) && (io.MousePos.x < (m_board_surface.x + DPIF(12.0f)))) {
1129 ImDrawList *draw = ImGui::GetWindowDrawList();
1130 draw->AddRectFilled(ImVec2(m_board_surface.x, m_menu_height),
1131 ImVec2(m_board_surface.x + DPIF(12.0f), m_board_surface.y + m_menu_height),
1132 ImColor(0x88888888));
1133 // DrawHex( draw, io.MousePos, DPIF(10.0f), ImColor(0xFF0000FF) );
1134 }
1135
1136 if (ImGui::IsMouseDragging(0)) {
1137 if ((m_dragging_token == 0) && (io.MouseClickedPos[0].x > m_board_surface.x) &&
1138 (io.MouseClickedPos[0].x < (m_board_surface.x + DPIF(20.0f))))
1139 m_dragging_token = 2; // own it.
1140 if (m_dragging_token == 2) {
1141 ImVec2 delta = ImGui::GetMouseDragDelta();
1142 if ((abs(delta.x) > 500) || (abs(delta.y) > 500)) {
1143 delta.x = 0;
1144 delta.y = 0;
1145 } // If the delta values are crazy just drop them (happens when panning
1146 // off screen). 500 arbritary chosen
1147 ImGui::ResetMouseDragDelta();
1148 m_board_surface.x += delta.x;
1149 m_info_surface.x = ds.x - m_board_surface.x;
1150 if (m_board_surface.x < ds.x * 0.66) {
1151 m_board_surface.x = ds.x * 0.66;
1152 m_info_surface.x = ds.x - m_board_surface.x;
1153 }
1154 if (delta.x > 0) m_needsRedraw = true;
1155 }
1156 } else {
1157 if (m_dragging_token == 2) {
1158 obvconfig.WriteInt("infoPanelWidth", m_info_surface.x);
1159 }
1160 m_dragging_token = 0;
1161 }
1162
1163 if (m_board) {
1164 ImGui::Columns(2);
1165 ImGui::Text("Pins: %ld", m_board->Pins().size());
1166 ImGui::Text("Parts: %ld", m_board->Components().size());
1167 ImGui::NextColumn();
1168 ImGui::Text("Nets: %ld", m_board->Nets().size());
1169 ImGui::TextWrapped("Size: %0.2f x %0.2f\"", m_boardWidth / 1000.0f, m_boardHeight / 1000.0f);
1170 ImGui::Columns(1);
1171 ImGui::Separator();
1172 if (ImGui::Checkbox("Zoom on selected net", &infoPanelCenterZoomNets)) {
1173 obvconfig.WriteBool("infoPanelCenterZoomNets", infoPanelCenterZoomNets);
1174 }
1175 if (ImGui::Checkbox("Select all parts on net", &infoPanelSelectPartsOnNet)) {
1176 obvconfig.WriteBool("infoPanelSelectPartsOnNet", infoPanelSelectPartsOnNet);
1177 }
1178 } else {
1179 ImGui::Text("No board currently loaded.");
1180 }
1181
1182 if (m_partHighlighted.size()) {
1183 ImGui::Separator();
1184 ImGui::TextUnformatted((std::to_string(m_partHighlighted.size()) + " parts selected").c_str());
1185
1186 for (auto part : m_partHighlighted) {
1187
1188 ImGui::Text(" ");
1189
1190 if (ImGui::SmallButton(part->name.c_str())) {
1191 if (!ComponentIsVisible(part)) FlipBoard();
1192 if (part->centerpoint.x && part->centerpoint.y) {
1193 ImVec2 psz;
1194
1195 /*
1196 * Check to see if we need to zoom BACK a bit to fit the part in to view
1197 */
1198 psz = ImVec2(abs(part->outline[2].x - part->outline[0].x) * m_scale / m_board_surface.x,
1199 abs(part->outline[2].y - part->outline[0].y) * m_scale / m_board_surface.y);
1200 if ((psz.x > 1) || (psz.y > 1)) {
1201 if (psz.x > psz.y) {
1202 m_scale /= (partZoomScaleOutFactor * psz.x);
1203 } else {
1204 m_scale /= (partZoomScaleOutFactor * psz.y);
1205 }
1206 }
1207
1208 SetTarget(part->centerpoint.x, part->centerpoint.y);
1209 } else {
1210 SetTarget(part->pins[0]->position.x, part->pins[0]->position.y);
1211 }
1212 m_needsRedraw = 1;
1213 }
1214 ImGui::SameLine();
1215 {
1216 char bn[128];
1217 snprintf(bn, sizeof(bn), "Z##%s", part->name.c_str());
1218 if (ImGui::SmallButton(bn)) {
1219 if (!ComponentIsVisible(part)) FlipBoard();
1220 if (part->centerpoint.x && part->centerpoint.y) {
1221 ImVec2 psz;
1222
1223 /*
1224 * Check to see if we need to zoom BACK a bit to fit the part in to view
1225 */
1226 psz = ImVec2(abs(part->outline[2].x - part->outline[0].x) * m_scale / m_board_surface.x,
1227 abs(part->outline[2].y - part->outline[0].y) * m_scale / m_board_surface.y);
1228 if (psz.x > psz.y) {
1229 m_scale /= (partZoomScaleOutFactor * psz.x);
1230 } else {
1231 m_scale /= (partZoomScaleOutFactor * psz.y);
1232 }
1233
1234 SetTarget(part->centerpoint.x, part->centerpoint.y);
1235 } else {
1236 SetTarget(part->pins[0]->position.x, part->pins[0]->position.y);
1237 }
1238 m_needsRedraw = 1;
1239 }
1240 }
1241
1242 ImGui::SameLine();
1243 ImGui::Text("%ld Pin(s)", part->pins.size());
1244 if (part->mfgcode.size()) ImGui::TextWrapped("%s", part->mfgcode.c_str());
1245
1246 /*
1247 * Generate the pin# and net table
1248 */
1249 ImGui::PushItemWidth(-1);
1250 std::string str = std::string("##") + part->name;
1251 ImVec2 listSize;
1252 int pc = part->pins.size();
1253 if (pc > 20) pc = 20;
1254 listSize = ImVec2(m_info_surface.x - DPIF(50), pc * ImGui::GetFontSize() * 1.45);
1255 ImGui::ListBoxHeader(str.c_str(), listSize); //, ImVec2(m_board_surface.x/3 -5, m_board_surface.y/2));
1256 for (auto pin : part->pins) {
1257 char ss[1024];
1258 snprintf(ss, sizeof(ss), "%4s %s", pin->number.c_str(), pin->net->name.c_str());
1259 if (ImGui::Selectable(ss, (pin == m_pinSelected))) {
1260 ClearAllHighlights();
1261
1262 if ((pin->type == Pin::kPinTypeNotConnected) || (pin->type == Pin::kPinTypeUnkown) || (pin->net->is_ground)) {
1263 m_partHighlighted.push_back(pin->component);
1264 // do nothing for now
1265 //
1266 } else {
1267 m_pinSelected = pin;
1268 for (auto p : m_partHighlighted) {
1269 pin->component->visualmode = pin->component->CVMNormal;
1270 };
1271 m_partHighlighted.push_back(pin->component);
1272 CenterZoomNet(pin->net->name);
1273 }
1274 m_needsRedraw = true;
1275 }
1276 ImGui::PushStyleColor(ImGuiCol_Border, ImColor(0xffeeeeee));
1277 ImGui::Separator();
1278 ImGui::PopStyleColor();
1279 }
1280 ImGui::ListBoxFooter();
1281 ImGui::PopItemWidth();
1282
1283 } // for each part in the list
1284 ImGui::Text(" ");
1285 ImGui::Separator();
1286 ImGui::Separator();
1287 ImGui::Separator();
1288 }
1289
1290 ImGui::End();
1291}
1292
1293void BoardView::ContextMenu(void) {
1294 bool dummy = true;
1295 static char contextbuf[10240] = "";
1296 static char contextbufnew[10240] = "";
1297 static std::string pin, partn, net;
1298 double tx, ty;
1299
1300 ImGuiIO &io = ImGui::GetIO();
1301
1302 ImVec2 pos = ScreenToCoord(m_showContextMenuPos.x, m_showContextMenuPos.y);
1303
1304 /*
1305 * So we don't have dozens of near-same-spot annotation points, we truncate
1306 * back to integer levels, which still gives us 1-thou precision
1307 */
1308 tx = trunc(pos.x);
1309 ty = trunc(pos.y);
1310
1311 /*
1312 * Originally the dialog was to follow the cursor but it proved to be an overkill
1313 * to try adjust things to keep it within the bounds of the window so as to not
1314 * lose the buttons.
1315 *
1316 * Now it's kept at a fixed point.
1317 */
1318 ImGui::SetNextWindowPos(ImVec2(DPIF(50), DPIF(50)));
1319
1320 if (ImGui::BeginPopupModal("Annotations", &dummy, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_ShowBorders)) {
1321
1322 if (m_showContextMenu) {
1323 contextbuf[0] = 0;
1324 m_showContextMenu = false;
1325 m_tooltips_enabled = false;
1326 // m_parent_occluded = true;
1327 for (auto &ann : m_annotations.annotations) ann.hovered = false;
1328 }
1329
1330 /*
1331 * For the new annotation possibility, we need to detect our various
1332 * attributes that we can bind to, such as pin, net, part
1333 */
1334 {
1335 /*
1336 * we're going to go through all the possible items we can annotate at this position and offer them
1337 */
1338
1339 float min_dist = m_pinDiameter * 1.0f;
1340
1341 /*
1342 * find the closest pin, starting at no more than
1343 * 1 radius away
1344 */
1345 min_dist *= min_dist; // all distance squared
1346 Pin *selection = nullptr;
1347 for (auto &pin : m_board->Pins()) {
1348 if (ComponentIsVisible(pin->component)) {
1349 float dx = pin->position.x - pos.x;
1350 float dy = pin->position.y - pos.y;
1351 float dist = dx * dx + dy * dy;
1352 if (dist < min_dist) {
1353 selection = pin.get();
1354 min_dist = dist;
1355 }
1356 }
1357 }
1358
1359 /*
1360 * If there was a pin selected, we can extract net/part off it
1361 */
1362
1363 if (selection != nullptr) {
1364 pin = selection->number;
1365 partn = selection->component->name;
1366 net = selection->net->name;
1367 } else {
1368
1369 /*
1370 * ELSE if we didn't get a pin selected, we can still
1371 * check for a part.
1372 *
1373 * There is a problem where we can be on two parts
1374 * but haven't decided what to do in such a situation
1375 */
1376
1377 for (auto &part : m_board->Components()) {
1378 int hit = 0;
1379 // auto p_part = part.get();
1380
1381 if (!ComponentIsVisible(part)) continue;
1382
1383 // Work out if the point is inside the hull
1384 {
1385 int i, j, n;
1386 outline_pt *poly;
1387
1388 n = 4;
1389 poly = part->outline;
1390
1391 for (i = 0, j = n - 1; i < n; j = i++) {
1392 if (((poly[i].y > pos.y) != (poly[j].y > pos.y)) &&
1393 (pos.x < (poly[j].x - poly[i].x) * (pos.y - poly[i].y) / (poly[j].y - poly[i].y) + poly[i].x))
1394 hit = !hit;
1395 }
1396 } // hull test
1397 if (hit) {
1398 partn = part->name;
1399
1400 ImGui::SameLine();
1401 }
1402
1403 } // for each part
1404 }
1405
1406 {
1407
1408 /*
1409 * For existing annotations
1410 */
1411 if (m_annotation_clicked_id >= 0) {
1412 if (m_annotationedit_retain || (m_annotation_clicked_id >= 0)) {
1413 Annotation ann = m_annotations.annotations[m_annotation_clicked_id];
1414 if (!m_annotationedit_retain) {
1415 snprintf(contextbuf, sizeof(contextbuf), "%s", ann.note.c_str());
1416 m_annotationedit_retain = true;
1417 m_annotationnew_retain = false;
1418 }
1419
1420 ImGui::Text("%c(%0.0f,%0.0f) %s, %s%c%s%c",
1421 m_current_side ? 'B' : 'T',
1422 tx,
1423 ty,
1424 ann.net.c_str(),
1425 ann.part.c_str(),
1426 ann.part.size() && ann.pin.size() ? '[' : ' ',
1427 ann.pin.c_str(),
1428 ann.part.size() && ann.pin.size() ? ']' : ' ');
1429 ImGui::InputTextMultiline("##annotationedit",
1430 contextbuf,
1431 sizeof(contextbuf),
1432 ImVec2(DPI(600), ImGui::GetTextLineHeight() * 8),
1433 0,
1434 NULL,
1435 contextbuf);
1436
1437 if (ImGui::Button("Update##1") || (ImGui::IsKeyPressed(SDLK_RETURN) && io.KeyShift)) {
1438 m_annotationedit_retain = false;
1439 m_annotations.Update(m_annotations.annotations[m_annotation_clicked_id].id, contextbuf);
1440 m_annotations.GenerateList();
1441 m_needsRedraw = true;
1442 m_tooltips_enabled = true;
1443 // m_parent_occluded = false;
1444 ImGui::CloseCurrentPopup();
1445 }
1446 ImGui::SameLine();
1447 if (ImGui::Button("Cancel##1")) {
1448 ImGui::CloseCurrentPopup();
1449 m_annotationnew_retain = false;
1450 m_tooltips_enabled = true;
1451 // m_parent_occluded = false;
1452 }
1453 ImGui::Separator();
1454 }
1455 }
1456
1457 /*
1458 * For generating a new annotation
1459 */
1460 if ((m_annotation_clicked_id < 0) && (m_annotationnew_retain == false)) {
1461 ImGui::Text("%c(%0.0f,%0.0f) %s %s%c%s%c",
1462 m_current_side ? 'B' : 'T',
1463 tx,
1464 ty,
1465 net.c_str(),
1466 partn.c_str(),
1467 partn.empty() || pin.empty() ? ' ' : '[',
1468 pin.c_str(),
1469 partn.empty() || pin.empty() ? ' ' : ']');
1470 }
1471 if ((m_annotation_clicked_id < 0) || ImGui::Button("Add New##1") || m_annotationnew_retain) {
1472 if (m_annotationnew_retain == false) {
1473 contextbufnew[0] = 0;
1474 m_annotationnew_retain = true;
1475 m_annotation_clicked_id = -1;
1476 m_annotationedit_retain = false;
1477 }
1478
1479 ImGui::Text("Create new annotation for: %c(%0.0f,%0.0f) %s %s%c%s%c",
1480 m_current_side ? 'B' : 'T',
1481 tx,
1482 ty,
1483 net.c_str(),
1484 partn.c_str(),
1485 partn.empty() || pin.empty() ? ' ' : '[',
1486 pin.c_str(),
1487 partn.empty() || pin.empty() ? ' ' : ']');
1488 ImGui::Spacing();
1489 ImGui::InputTextMultiline("New##annotationnew",
1490 contextbufnew,
1491 sizeof(contextbufnew),
1492 ImVec2(DPI(600), ImGui::GetTextLineHeight() * 8),
1493 0,
1494 NULL,
1495 contextbufnew);
1496
1497 if (ImGui::Button("Apply##1") || (ImGui::IsKeyPressed(SDLK_RETURN) && io.KeyShift)) {
1498 m_tooltips_enabled = true;
1499 m_annotationnew_retain = false;
1500 if (debug) fprintf(stderr, "DATA:'%s'\n\n", contextbufnew);
1501
1502 m_annotations.Add(m_current_side, tx, ty, net.c_str(), partn.c_str(), pin.c_str(), contextbufnew);
1503 m_annotations.GenerateList();
1504 m_needsRedraw = true;
1505
1506 ImGui::CloseCurrentPopup();
1507 }
1508 /*
1509 ImGui::SameLine();
1510 if (ImGui::Button("Cancel")) {
1511 ImGui::CloseCurrentPopup();
1512 m_tooltips_enabled = true;
1513 m_annotationnew_retain = false;
1514 }
1515 */
1516 } else {
1517 ImGui::SameLine();
1518 }
1519
1520 if ((m_annotation_clicked_id >= 0) && (ImGui::Button("Remove"))) {
1521 m_annotations.Remove(m_annotations.annotations[m_annotation_clicked_id].id);
1522 m_annotations.GenerateList();
1523 m_needsRedraw = true;
1524 // m_parent_occluded = false;
1525 ImGui::CloseCurrentPopup();
1526 }
1527 }
1528
1529 // the position.
1530 }
1531
1532 ImGui::SameLine();
1533 if (ImGui::Button("Cancel##2") || ImGui::IsKeyPressed(SDLK_ESCAPE)) {
1534 m_annotationnew_retain = false;
1535 m_annotationedit_retain = false;
1536 m_tooltips_enabled = true;
1537 // m_parent_occluded = false;
1538 ImGui::CloseCurrentPopup();
1539 }
1540 ImGui::EndPopup();
1541 // m_tooltips_enabled = true;
1542 }
1543
1544 /** if the dialog was closed by using the (X) icon **/
1545 if (!dummy) {
1546 m_annotationnew_retain = false;
1547 m_annotationedit_retain = false;
1548 m_tooltips_enabled = true;
1549 // m_parent_occluded = false;
1550 }
1551}
1552
1553std::pair<SharedVector<Component>, SharedVector<Net>> BoardView::SearchPartsAndNets(const char *search, int limit) {
1554 SharedVector<Component> parts;
1555 SharedVector<Net> nets;
1556 if (m_searchComponents) parts = searcher.parts(search, limit);
1557 if (m_searchNets) nets = searcher.nets(search, limit);
1558 return {parts, nets};
1559}
1560
1561const char *getcname(const std::string &name) {
1562 return name.c_str();
1563}
1564
1565template<class T> const char *getcname(const T &t) {
1566 return t->name.c_str();
1567}
1568
1569template<class T> void BoardView::ShowSearchResults(std::vector<T> results, char *search, int &limit, void (BoardView::*onSelect)(const char *)) {
1570 for (auto &r : results) {
1571 const char *cname = getcname(r);
1572 if (ImGui::Selectable(cname, false)) {
1573 (this->*onSelect)(cname);
1574 snprintf(search, 128, "%s", cname);
1575 limit = 0;
1576 }
1577 limit--;
1578 }
1579}
1580
1581void BoardView::SearchColumnGenerate(const std::string& title, std::pair<SharedVector<Component>, SharedVector<Net>> results, char *search, int limit) {
1582 ImGui::ListBoxHeader(title.c_str());
1583
1584 if (m_searchComponents) {
1585 if (results.first.empty() && (!m_searchNets || results.second.empty())) { // show suggestions only if there is no result at all
1586 auto s = scparts.suggest(search);
1587 if (s.size() > 0) {
1588 ImGui::Text("Did you mean...");
1589 ShowSearchResults(s, search, limit, &BoardView::FindComponent);
1590 }
1591 } else
1592 ShowSearchResults(results.first, search, limit, &BoardView::FindComponent);
1593 }
1594
1595 if (m_searchNets) {
1596 if (results.second.empty() && (!m_searchComponents || results.first.empty())) {
1597 auto s = scnets.suggest(search);
1598 if (s.size() > 0) {
1599 ImGui::Text("Did you mean...");
1600 ShowSearchResults(s, search, limit, &BoardView::FindNet);
1601 }
1602 } else
1603 ShowSearchResults(results.second, search, limit, &BoardView::FindNet);
1604 }
1605
1606 ImGui::ListBoxFooter();
1607}
1608
1609void BoardView::SearchComponent(void) {
1610 bool dummy = true;
1611
1612 ImGui::SetNextWindowPos(ImVec2(-FLT_MAX, DPI(100))); // FIXME This will need changing in the future when ImGui gets proper
1613 // per-axis centering ( see https://github.com/ocornut/imgui/issues/770 )
1614 // ImGui::SetNextWindowPosCenter();
1615 if (ImGui::BeginPopupModal("Search for Component / Network",
1616 &dummy,
1617 ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings |
1618 ImGuiWindowFlags_ShowBorders)) {
1619 // char cs[128];
1620 const char *first_button[] = { m_search[0], m_search[1], m_search[2] };
1621
1622 if (m_showSearch) {
1623 m_showSearch = false;
1624 m_tooltips_enabled = false;
1625 // fprintf(stderr, "Tooltips disabled\n");
1626 }
1627
1628 // Column 1, implied.
1629 //
1630 //
1631 if (ImGui::Button("Search")) {
1632 // FindComponent(first_button);
1633 m_tooltips_enabled = true;
1634 SearchCompound(first_button[0]);
1635 SearchCompoundNoClear(first_button[1]);
1636 SearchCompoundNoClear(first_button[2]);
1637 CenterZoomSearchResults();
1638 ImGui::CloseCurrentPopup();
1639 } // search button
1640
1641 ImGui::SameLine();
1642 if (ImGui::Button("Reset")) {
1643 FindComponent("");
1644 for (int i = 0; i < 3; i++)
1645 m_search[i][0] = '\0';
1646 } // reset button
1647
1648 ImGui::SameLine();
1649 if (ImGui::Button("Exit") || ImGui::IsKeyPressed(SDLK_ESCAPE)) {
1650 FindComponent("");
1651 for (int i = 0; i < 3; i++)
1652 m_search[i][0] = '\0';
1653 m_tooltips_enabled = true;
1654 ImGui::CloseCurrentPopup();
1655 } // exit button
1656
1657 ImGui::SameLine();
1658 // ImGui::Dummy(ImVec2(DPI(200), 1));
1659 // ImGui::SameLine();
1660 ImGui::PushItemWidth(-1);
1661 ImGui::Text("ENTER: Search, ESC: Exit, TAB: next field");
1662
1663 ImGui::Separator();
1664 ImGui::Checkbox("Components", &m_searchComponents);
1665
1666 ImGui::SameLine();
1667 ImGui::Checkbox("Nets", &m_searchNets);
1668
1669 {
1670 ImGui::SameLine();
1671 ImGui::Text(" Search mode: ");
1672 ImGui::SameLine();
1673 if (ImGui::RadioButton("Substring", searcher.isMode(SearchMode::Sub))) {
1674 searcher.setMode(SearchMode::Sub);
1675 }
1676 ImGui::SameLine();
1677 if (ImGui::RadioButton("Prefix", searcher.isMode(SearchMode::Prefix))) {
1678 searcher.setMode(SearchMode::Prefix);
1679 }
1680 ImGui::SameLine();
1681 ImGui::PushItemWidth(-1);
1682 if (ImGui::RadioButton("Whole", searcher.isMode(SearchMode::Whole))) {
1683 searcher.setMode(SearchMode::Whole);
1684 }
1685 ImGui::PopItemWidth();
1686 }
1687
1688 ImGui::Separator();
1689
1690 ImGui::Columns(3);
1691
1692 for (int i = 1; i <= 3; i++) {
1693 std::string istr = std::to_string(i);
1694 std::string title = "Item #" + istr;
1695 std::string searchLabel = "##search" + istr;
1696 ImGui::Text("%s", title.c_str());
1697
1698 ImGui::PushItemWidth(-1);
1699
1700 bool searching = m_search[i-1][0] != '\0'; // Text typed in the search box
1701 auto results = SearchPartsAndNets(m_search[i-1], 30); // Perform the search for both nets and parts
1702 bool hasResults = !results.first.empty() || !results.second.empty(); // We found some nets or some parts
1703
1704 if (searching && !hasResults) ImGui::PushStyleColor(ImGuiCol_FrameBg, ImColor(0xFF6666FF));
1705 auto ret = ImGui::InputText(searchLabel.c_str(), m_search[i-1], 128, ImGuiInputTextFlags_CharsNoBlank|(m_search[0]?ImGuiInputTextFlags_AutoSelectAll:0));
1706 if (searching && !hasResults) ImGui::PopStyleColor();
1707
1708 if (ret) SearchCompound(m_search[i-1]);
1709
1710 ImGui::PopItemWidth();
1711
1712 if (i == 1 && ImGui::IsRootWindowOrAnyChildFocused() && !ImGui::IsAnyItemActive() && !ImGui::IsMouseClicked(0)) {
1713 ImGui::SetKeyboardFocusHere(-1);
1714 } // set keyboard focus
1715
1716 ImGui::PushItemWidth(-1);
1717 if (searching) SearchColumnGenerate("##SC" + istr, results, m_search[i-1], 30);
1718 ImGui::PopItemWidth();
1719 if (i == 1) ImGui::PushItemWidth(DPI(500));
1720 else if (i == 2) ImGui::PopItemWidth();
1721
1722 ImGui::NextColumn();
1723 }
1724
1725 ImGui::PopItemWidth();
1726
1727 ImGui::Columns(1); // reset back to single column mode
1728 ImGui::Separator();
1729
1730 // Enter and Esc close the search:
1731 if (ImGui::IsKeyPressed(SDLK_RETURN)) {
1732 // SearchCompound(first_button);
1733 // SearchCompoundNoClear(first_button2);
1734 // SearchCompoundNoClear(first_button3);
1735 SearchCompound(m_search[0]);
1736 SearchCompoundNoClear(m_search[1]);
1737 SearchCompoundNoClear(m_search[2]);
1738 CenterZoomSearchResults();
1739 ImGui::CloseCurrentPopup();
1740 m_tooltips_enabled = true;
1741 } // response to keyboard ENTER
1742
1743 ImGui::EndPopup();
1744 }
1745 if (!dummy) {
1746 m_tooltips_enabled = true;
1747 }
1748}
1749
1750void BoardView::ClearAllHighlights(void) {
1751 m_pinSelected = nullptr;
1752 FindNet("");
1753 FindComponent("");
1754 m_search[0][0] = '\0';
1755 m_search[1][0] = '\0';
1756 m_search[2][0] = '\0';
1757 m_needsRedraw = true;
1758 m_tooltips_enabled = true;
1759 if (m_board != NULL) {
1760 for (auto part : m_board->Components()) part->visualmode = part->CVMNormal;
1761 }
1762 m_partHighlighted.clear();
1763 m_pinHighlighted.clear();
1764}
1765
1766/** UPDATE Logic region
1767 *
1768 *
1769 *
1770 *
1771 */
1772void BoardView::Update() {
1773 bool open_file = false;
1774 // ImGuiIO &io = ImGui::GetIO();
1775 char *preset_filename = NULL;
1776 ImGuiIO &io = ImGui::GetIO();
1777
1778 /**
1779 * ** FIXME
1780 * This should be handled in the keyboard section, not here
1781 */
1782 if ((io.KeyCtrl) && ImGui::IsKeyPressed(SDLK_o)) {
1783 open_file = true;
1784 // the dialog will likely eat our WM_KEYUP message for CTRL and O:
1785 io.KeysDown[SDL_SCANCODE_RCTRL] = false;
1786 io.KeysDown[SDL_SCANCODE_LCTRL] = false;
1787 io.KeysDown[SDLK_o] = false;
1788 }
1789
1790 if ((io.KeyCtrl) && ImGui::IsKeyPressed(SDLK_r)) {
1791 LoadFile(fhistory.history[0],1);
1792 // the dialog will likely eat our WM_KEYUP message for CTRL and r:
1793 io.KeysDown[SDL_SCANCODE_RCTRL] = false;
1794 io.KeysDown[SDL_SCANCODE_LCTRL] = false;
1795 io.KeysDown[SDLK_r] = false;
1796 }
1797
1798 if ((io.KeyCtrl) && ImGui::IsKeyPressed(SDLK_q)) {
1799 m_wantsQuit = true;
1800 }
1801
1802 if (ImGui::BeginMainMenuBar()) {
1803 m_menu_height = ImGui::GetWindowHeight();
1804
1805 /*
1806 * Create these dialogs, but they're not actually
1807 * visible until they're called up by the respective
1808 * flags.
1809 */
1810 SearchComponent();
1811 HelpControls();
1812 HelpAbout();
1813 ColorPreferences();
1814 Preferences();
1815 ContextMenu();
1816
1817 if (ImGui::BeginMenu("File")) {
1818 if (ImGui::MenuItem("Open", "Ctrl+O")) {
1819 open_file = true;
1820 }
1821
1822 if (ImGui::MenuItem("Reload", "Ctrl+R")) {
1823 //check if file exists
1824 //keep viewport parameters dont reset
1825 LoadFile(fhistory.history[0],1);
1826 }
1827
1828 /// Generate file history - PLD20160618-2028
1829 ImGui::Separator();
1830 {
1831 int i;
1832 for (i = 0; i < fhistory.count; i++) {
1833 if (ImGui::MenuItem(fhistory.Trim_filename(fhistory.history[i], 2))) {
1834 open_file = true;
1835 preset_filename = fhistory.history[i];
1836 }
1837 }
1838 }
1839 ImGui::Separator();
1840
1841 if (ImGui::MenuItem("Part/Net Search", "Ctrl+F, /")) {
1842 if (m_validBoard) m_showSearch = true;
1843 }
1844
1845 ImGui::Separator();
1846
1847 if (ImGui::MenuItem("Program Preferences")) {
1848 m_showPreferences = true;
1849 }
1850
1851 if (ImGui::MenuItem("Colour Preferences")) {
1852 m_showColorPreferences = true;
1853 }
1854
1855 ImGui::Separator();
1856
1857 if (ImGui::MenuItem("Quit")) {
1858 m_wantsQuit = true;
1859 }
1860 ImGui::EndMenu();
1861 }
1862 if (ImGui::BeginMenu("View")) {
1863 if (ImGui::MenuItem("Flip board", "<Space>")) {
1864 FlipBoard();
1865 }
1866 if (ImGui::MenuItem("Rotate CW", ".")) {
1867 Rotate(1);
1868 }
1869 if (ImGui::MenuItem("Rotate CCW", ",")) {
1870 Rotate(-1);
1871 }
1872 if (ImGui::MenuItem("Reset View", "x NP-5")) { // actually want this to be numpad 5
1873 CenterView();
1874 }
1875 if (ImGui::MenuItem("Mirror Board", "m")) {
1876 Mirror();
1877 }
1878
1879 if (ImGui::MenuItem("Toggle Pin Display", "p")) {
1880 showPins ^= 1;
1881 m_needsRedraw = true;
1882 }
1883
1884 if (ImGui::MenuItem("Show Info Panel", "i")) {
1885 showInfoPanel ^= 1;
1886 obvconfig.WriteBool("showInfoPanel", showInfoPanel ? true : false);
1887 m_needsRedraw = true;
1888 }
1889
1890 ImGui::Separator();
1891 if (ImGui::Checkbox("Show FPS", &showFPS)) {
1892 obvconfig.WriteBool("showFPS", showFPS);
1893 m_needsRedraw = true;
1894 }
1895
1896 if (ImGui::Checkbox("Show Position", &showPosition)) {
1897 obvconfig.WriteBool("showNetWeb", showPosition);
1898 m_needsRedraw = true;
1899 }
1900
1901 if (ImGui::Checkbox("Net web", &showNetWeb)) {
1902 obvconfig.WriteBool("showNetWeb", showNetWeb);
1903 m_needsRedraw = true;
1904 }
1905
1906 if (ImGui::Checkbox("Annotations", &showAnnotations)) {
1907 obvconfig.WriteBool("annotations", showAnnotations);
1908 m_needsRedraw = true;
1909 }
1910
1911 if (ImGui::Checkbox("Board fill", &boardFill)) {
1912 obvconfig.WriteBool("boardFill", boardFill);
1913 m_needsRedraw = true;
1914 }
1915
1916 if (ImGui::Checkbox("Part fill", &fillParts)) {
1917 obvconfig.WriteBool("fillParts", fillParts);
1918 m_needsRedraw = true;
1919 }
1920
1921 ImGui::Separator();
1922
1923 if (ImGui::MenuItem("Info Pane", "i")) {
1924 showInfoPanel = !showInfoPanel;
1925 }
1926 if (ImGui::MenuItem("Net List", "l")) {
1927 m_showNetList = !m_showNetList;
1928 }
1929 if (ImGui::MenuItem("Part List", "k")) {
1930 m_showPartList = !m_showPartList;
1931 }
1932
1933 ImGui::EndMenu();
1934 }
1935
1936 if (ImGui::BeginMenu("Help##1")) {
1937 if (ImGui::MenuItem("Controls")) {
1938 m_showHelpControls = true;
1939 }
1940 if (ImGui::MenuItem("About")) {
1941 m_showHelpAbout = true;
1942 }
1943 ImGui::EndMenu();
1944 }
1945
1946 ImGui::SameLine();
1947 ImGui::Dummy(ImVec2(DPI(10), 1));
1948 ImGui::SameLine();
1949 if (ImGui::Checkbox("Annotations", &showAnnotations)) {
1950 obvconfig.WriteBool("showAnnotations", showAnnotations);
1951 m_needsRedraw = true;
1952 }
1953
1954 ImGui::SameLine();
1955 if (ImGui::Checkbox("Netweb", &showNetWeb)) {
1956 obvconfig.WriteBool("showNetWeb", showNetWeb);
1957 m_needsRedraw = true;
1958 }
1959
1960 ImGui::SameLine();
1961 {
1962 if (ImGui::Checkbox("Pins", &showPins)) {
1963 obvconfig.WriteBool("showPins", showPins);
1964 m_needsRedraw = true;
1965 }
1966 }
1967
1968 ImGui::SameLine();
1969 ImGui::Dummy(ImVec2(DPI(40), 1));
1970
1971 ImGui::SameLine();
1972 if (ImGui::Button(" - ")) {
1973 Zoom(m_board_surface.x / 2, m_board_surface.y / 2, -zoomFactor);
1974 }
1975 ImGui::SameLine();
1976 if (ImGui::Button(" + ")) {
1977 Zoom(m_board_surface.x / 2, m_board_surface.y / 2, zoomFactor);
1978 }
1979 ImGui::SameLine();
1980 ImGui::Dummy(ImVec2(DPI(20), 1));
1981 ImGui::SameLine();
1982 if (ImGui::Button("-")) {
1983 Zoom(m_board_surface.x / 2, m_board_surface.y / 2, -zoomFactor / zoomModifier);
1984 }
1985 ImGui::SameLine();
1986 if (ImGui::Button("+")) {
1987 Zoom(m_board_surface.x / 2, m_board_surface.y / 2, zoomFactor / zoomModifier);
1988 }
1989
1990 ImGui::SameLine();
1991 ImGui::Dummy(ImVec2(DPI(20), 1));
1992 ImGui::SameLine();
1993 if (ImGui::Button(" < ")) {
1994 Rotate(-1);
1995 }
1996
1997 ImGui::SameLine();
1998 if (ImGui::Button(" ^ ")) {
1999 FlipBoard();
2000 }
2001
2002 ImGui::SameLine();
2003 if (ImGui::Button(" > ")) {
2004 Rotate(1);
2005 }
2006
2007 ImGui::SameLine();
2008 if (ImGui::Button("X")) {
2009 CenterView();
2010 }
2011
2012 ImGui::SameLine();
2013 if (ImGui::Button("CLEAR")) {
2014 ClearAllHighlights();
2015 }
2016
2017 /*
2018 ImGui::SameLine();
2019 ImVec2 tz = ImGui::CalcTextSize("Search ");
2020 ImGui::PushItemWidth(-tz.x);
2021 ImGui::Text("Search");
2022 ImGui::SameLine();
2023 ImGui::PushItemWidth(-DPI(1));
2024 if (ImGui::InputText("##ansearch", m_search, 128, 0)) {
2025 }
2026 ImGui::PopItemWidth();
2027 */
2028
2029 if (m_showContextMenu && m_file && showAnnotations) {
2030 ImGui::OpenPopup("Annotations");
2031 }
2032
2033 if (m_showHelpAbout) {
2034 ImGui::OpenPopup("About");
2035 }
2036 if (m_showHelpControls) {
2037 ImGui::OpenPopup("Controls");
2038 }
2039
2040 if (m_showColorPreferences) {
2041 ImGui::OpenPopup("Colour Preferences");
2042 }
2043
2044 if (m_showPreferences) {
2045 ImGui::OpenPopup("Preferences");
2046 }
2047
2048 if (m_showSearch && m_file) {
2049 ImGui::OpenPopup("Search for Component / Network");
2050 }
2051 if (m_lastFileOpenWasInvalid) {
2052 ImGui::OpenPopup("Error opening file");
2053 m_lastFileOpenWasInvalid = false;
2054 }
2055
2056 if (ImGui::BeginPopupModal("Error opening file")) {
2057 ImGui::Text("There was an error opening the file: %s", m_lastFileOpenName.c_str());
2058 if (check_fileext(m_lastFileOpenName, ".fz")) {
2059 int i;
2060 ImGui::Separator();
2061 ImGui::Text("FZKey:");
2062 for (i = 0; i < 44; i++) {
2063 // snprintf(s, sizeof(s), "%08x %08x %08x %08x", FZKey[i], FZKey[i + 1], FZKey[i + 2], FZKey[i + 3]);
2064 ImGui::Text("%08x %08x %08x %08x", FZKey[i], FZKey[i + 1], FZKey[i + 2], FZKey[i + 3]);
2065 // ImGui::Text(s);
2066 i += 3;
2067 }
2068 }
2069 // TODO: error details? -- would need the loader to say what's wrong.
2070 if (ImGui::Button("OK")) {
2071 ImGui::CloseCurrentPopup();
2072 }
2073 ImGui::EndPopup();
2074 }
2075 ImGui::EndMainMenuBar();
2076 }
2077
2078 if (open_file) {
2079 std::string filename;
2080
2081 if (preset_filename) {
2082 filename = preset_filename;
2083 preset_filename = NULL;
2084 } else {
2085 filename = show_file_picker();
2086
2087 ImGuiIO &io = ImGui::GetIO();
2088 io.MouseDown[0] = false;
2089 io.MouseClicked[0] = false;
2090 io.MouseClickedPos[0] = ImVec2(0, 0);
2091 }
2092
2093 if (!filename.empty()) {
2094 LoadFile(filename, 0);
2095 }
2096 }
2097
2098 ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
2099 ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse |
2100 ImGuiWindowFlags_NoSavedSettings;
2101
2102 ImGuiWindowFlags draw_surface_flags = flags | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus;
2103
2104 /*
2105 * Status footer
2106 */
2107 m_status_height = (DPIF(10.0f) + ImGui::GetFontSize());
2108 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(DPIF(4.0f), DPIF(3.0f)));
2109 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
2110 ImGui::SetNextWindowPos(ImVec2(0, io.DisplaySize.y - m_status_height));
2111 ImGui::SetNextWindowSize(ImVec2(io.DisplaySize.x, m_status_height));
2112 ImGui::Begin("status", nullptr, flags | ImGuiWindowFlags_NoFocusOnAppearing);
2113 if (m_file && m_board && m_pinSelected) {
2114 auto pin = m_pinSelected;
2115 ImGui::Text("Part: %s Pin: %s Net: %s Probe: %d (%s.)",
2116 pin->component->name.c_str(),
2117 pin->number.c_str(),
2118 pin->net->name.c_str(),
2119 pin->net->number,
2120 pin->component->mount_type_str().c_str());
2121 } else {
2122 ImVec2 spos = ImGui::GetMousePos();
2123 ImVec2 pos = ScreenToCoord(spos.x, spos.y);
2124 if (showFPS == true) {
2125 ImGui::Text("FPS: %0.0f ", ImGui::GetIO().Framerate);
2126 ImGui::SameLine();
2127 }
2128
2129 if (debug) {
2130 ImGui::Text("AnnID:%d ", m_annotation_clicked_id);
2131 ImGui::SameLine();
2132 }
2133
2134 if (showPosition == true) {
2135 ImGui::Text("Position: %0.3f\", %0.3f\" (%0.2f, %0.2fmm)", pos.x / 1000, pos.y / 1000, pos.x * 0.0254, pos.y * 0.0254);
2136 ImGui::SameLine();
2137 }
2138
2139 {
2140 if (m_validBoard) {
2141 ImVec2 s = ImGui::CalcTextSize(fhistory.history[0]);
2142 ImGui::SameLine(ImGui::GetWindowWidth() - s.x - 20);
2143 ImGui::Text("%s", fhistory.history[0]);
2144 ImGui::SameLine();
2145 }
2146 }
2147 ImGui::Text(" ");
2148 }
2149 ImGui::End();
2150 ImGui::PopStyleVar();
2151 ImGui::PopStyleVar();
2152
2153 if (!showInfoPanel) {
2154 m_board_surface = ImVec2(io.DisplaySize.x, io.DisplaySize.y - (m_status_height + m_menu_height));
2155 } else {
2156 m_board_surface = ImVec2(io.DisplaySize.x - m_info_surface.x, io.DisplaySize.y - (m_status_height + m_menu_height));
2157 }
2158 /*
2159 * Drawing surface, where the actual PCB/board is plotted out
2160 */
2161 ImGui::SetNextWindowPos(ImVec2(0, m_menu_height));
2162 if (io.DisplaySize.x != m_lastWidth || io.DisplaySize.y != m_lastHeight) {
2163 // m_lastWidth = io.DisplaySize.x;
2164 // m_lastHeight = io.DisplaySize.y;
2165 m_lastWidth = m_board_surface.x;
2166 m_lastHeight = m_board_surface.y;
2167 m_needsRedraw = true;
2168 }
2169
2170 ImGui::SetNextWindowSize(m_board_surface);
2171
2172 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
2173 ImGui::PushStyleColor(ImGuiCol_WindowBg, ImColor(m_colors.backgroundColor));
2174
2175 ImGui::Begin("surface", nullptr, draw_surface_flags);
2176 if (m_validBoard) {
2177 HandleInput();
2178 DrawBoard();
2179 }
2180 ImGui::End();
2181 ImGui::PopStyleColor();
2182
2183 // Overlay
2184 RenderOverlay();
2185
2186 ImGui::PopStyleVar();
2187
2188} // main menu bar
2189
2190void BoardView::Zoom(float osd_x, float osd_y, float zoom) {
2191 ImVec2 target;
2192 ImVec2 coord;
2193 ImGuiIO &io = ImGui::GetIO();
2194
2195 if (io.KeyCtrl) zoom /= zoomModifier;
2196
2197 target.x = osd_x;
2198 target.y = osd_y;
2199 coord = ScreenToCoord(target.x, target.y);
2200
2201 // Adjust the scale of the whole view, then get the new coordinates ( as
2202 // CoordToScreen utilises m_scale )
2203 m_scale = m_scale * powf(2.0f, zoom);
2204 ImVec2 dtarget = CoordToScreen(coord.x, coord.y);
2205
2206 ImVec2 td = ScreenToCoord(target.x - dtarget.x, target.y - dtarget.y, 0);
2207 m_dx += td.x;
2208 m_dy += td.y;
2209 m_needsRedraw = true;
2210}
2211
2212void BoardView::Pan(int direction, int amount) {
2213 ImGuiIO &io = ImGui::GetIO();
2214#define DIR_UP 1
2215#define DIR_DOWN 2
2216#define DIR_LEFT 3
2217#define DIR_RIGHT 4
2218
2219 amount = amount / m_scale;
2220
2221 if (io.KeyCtrl) amount /= panModifier;
2222
2223 switch (direction) {
2224 case DIR_UP: amount = -amount;
2225 case DIR_DOWN:
2226 if ((m_current_side) && (m_rotation % 2)) amount = -amount;
2227 switch (m_rotation) {
2228 case 0: m_dy += amount; break;
2229 case 1: m_dx -= amount; break;
2230 case 2: m_dy -= amount; break;
2231 case 3: m_dx += amount; break;
2232 }
2233 break;
2234 case DIR_LEFT: amount = -amount;
2235 case DIR_RIGHT:
2236 if ((m_current_side) && ((m_rotation % 2) == 0)) amount = -amount;
2237 switch (m_rotation) {
2238 case 0: m_dx -= amount; break;
2239 case 1: m_dy -= amount; break;
2240 case 2: m_dx += amount; break;
2241 case 3: m_dy += amount; break;
2242 }
2243 break;
2244 }
2245
2246 m_draggingLastFrame = true;
2247 m_needsRedraw = true;
2248}
2249
2250/*
2251 * This HandleInput() function isn't actually a generic input handler
2252 * it's meant specifically for the board draw surface only. The inputs
2253 * for menus is handled within the menu generation itself.
2254 */
2255void BoardView::HandleInput() {
2256
2257#ifdef _WIN32
2258#define KM(x) (x)
2259#else
2260#define KM(x) (((x)&0xFF) | 0x100)
2261#endif
2262
2263 if (!m_board || (!m_file)) return;
2264
2265 const ImGuiIO &io = ImGui::GetIO();
2266
2267 if (ImGui::IsWindowHovered()) {
2268
2269 if (ImGui::IsMouseDragging(0)) {
2270 if ((m_dragging_token == 0) && (io.MouseClickedPos[0].x < m_board_surface.x)) m_dragging_token = 1; // own it.
2271 if (m_dragging_token == 1) {
2272 // if ((io.MouseClickedPos[0].x < m_info_surface.x)) {
2273 ImVec2 delta = ImGui::GetMouseDragDelta();
2274 if ((abs(delta.x) > 500) || (abs(delta.y) > 500)) {
2275 delta.x = 0;
2276 delta.y = 0;
2277 } // If the delta values are crazy just drop them (happens when panning
2278 // off screen). 500 arbritary chosen
2279 ImGui::ResetMouseDragDelta();
2280 ImVec2 td = ScreenToCoord(delta.x, delta.y, 0);
2281 m_dx += td.x;
2282 m_dy += td.y;
2283 m_draggingLastFrame = true;
2284 m_needsRedraw = true;
2285 }
2286 } else {
2287 m_dragging_token = 0;
2288
2289 if (m_lastFileOpenWasInvalid == false) {
2290 // Conext menu
2291 if (!m_lastFileOpenWasInvalid && m_file && m_board && ImGui::IsMouseClicked(1)) {
2292 if (showAnnotations) {
2293 // Build context menu here, for annotations and inspection
2294 //
2295 ImVec2 spos = ImGui::GetMousePos();
2296 if (AnnotationIsHovered()) m_annotation_clicked_id = m_annotation_last_hovered;
2297
2298 m_annotationedit_retain = false;
2299 m_showContextMenu = true;
2300 m_showContextMenuPos = spos;
2301 m_tooltips_enabled = false;
2302 m_needsRedraw = true;
2303 if (debug) fprintf(stderr, "context click request at (%f %f)\n", spos.x, spos.y);
2304 }
2305
2306 // Flip the board with the middle click
2307 } else if (!m_lastFileOpenWasInvalid && m_file && m_board && ImGui::IsMouseReleased(2)) {
2308 FlipBoard();
2309
2310 // Else, click to select pin
2311 } else if (!m_lastFileOpenWasInvalid && m_file && m_board && ImGui::IsMouseReleased(0) && !m_draggingLastFrame) {
2312 ImVec2 spos = ImGui::GetMousePos();
2313 ImVec2 pos = ScreenToCoord(spos.x, spos.y);
2314
2315 m_needsRedraw = true;
2316
2317 // threshold to within a pin's diameter of the pin center
2318 // float min_dist = m_pinDiameter * 1.0f;
2319 float min_dist = m_pinDiameter / 2.0f;
2320 min_dist *= min_dist; // all distance squared
2321 std::shared_ptr<Pin> selection = nullptr;
2322 for (auto &pin : m_board->Pins()) {
2323 if (ComponentIsVisible(pin->component)) {
2324 float dx = pin->position.x - pos.x;
2325 float dy = pin->position.y - pos.y;
2326 float dist = dx * dx + dy * dy;
2327 if ((dist < (pin->diameter * pin->diameter)) && (dist < min_dist)) {
2328 selection = pin;
2329 min_dist = dist;
2330 }
2331 }
2332 }
2333
2334 m_pinSelected = selection;
2335 if (m_pinSelected) {
2336 if (!io.KeyCtrl) {
2337 for (auto p : m_board->Components()) {
2338 p->visualmode = p->CVMNormal;
2339 }
2340 m_partHighlighted.resize(0);
2341 m_pinHighlighted.resize(0);
2342 }
2343 m_pinSelected->component->visualmode = m_pinSelected->component->CVMSelected;
2344 m_partHighlighted.push_back(m_pinSelected->component);
2345 }
2346
2347 if (m_pinSelected == nullptr) {
2348 bool any_hits = false;
2349
2350 for (auto &part : m_board->Components()) {
2351 int hit = 0;
2352 // auto p_part = part.get();
2353
2354 if (!ComponentIsVisible(part)) continue;
2355
2356 // Work out if the point is inside the hull
2357 {
2358 int i, j, n;
2359 outline_pt *poly;
2360
2361 n = 4;
2362 poly = part->outline;
2363
2364 for (i = 0, j = n - 1; i < n; j = i++) {
2365 if (((poly[i].y > pos.y) != (poly[j].y > pos.y)) &&
2366 (pos.x <
2367 (poly[j].x - poly[i].x) * (pos.y - poly[i].y) / (poly[j].y - poly[i].y) + poly[i].x))
2368 hit ^= 1;
2369 }
2370 }
2371
2372 if (hit) {
2373 any_hits = true;
2374
2375 bool partInList = contains(part, m_partHighlighted);
2376
2377 /*
2378 * If the CTRL key isn't held down, then we have to
2379 * flush any existing highlighted parts
2380 */
2381 if (io.KeyCtrl) {
2382 if (!partInList) {
2383 m_partHighlighted.push_back(part);
2384 part->visualmode = part->CVMSelected;
2385 } else {
2386 remove(part, m_partHighlighted);
2387 part->visualmode = part->CVMNormal;
2388 }
2389
2390 } else {
2391 for (auto p : m_board->Components()) {
2392 p->visualmode = p->CVMNormal;
2393 }
2394 m_partHighlighted.resize(0);
2395 m_pinHighlighted.resize(0);
2396 if (!partInList) {
2397 m_partHighlighted.push_back(part);
2398 part->visualmode = part->CVMSelected;
2399 }
2400 }
2401
2402 /*
2403 * If this part has a non-selected visual mode (normal)
2404 * AND it's not in the existing part list, then add it
2405 */
2406 /*
2407 if (part->visualmode == part->CVMNormal) {
2408 if (!partInList) {
2409 m_partHighlighted.push_back(p_part);
2410 }
2411 }
2412
2413 part->visualmode++;
2414 part->visualmode %= part->CVMModeCount;
2415
2416 if (part->visualmode == part->CVMNormal) {
2417 remove(*part, m_partHighlighted);
2418 }
2419 */
2420 } // if hit
2421 } // for each part on the board
2422
2423 /*
2424 * If we aren't holding down CTRL and we click to a
2425 * non pin, non part area, then we clear everything
2426 */
2427 if ((!any_hits) && (!io.KeyCtrl)) {
2428 for (auto &part : m_board->Components()) {
2429 // auto p = part.get();
2430 part->visualmode = part->CVMNormal;
2431 }
2432 m_partHighlighted.clear();
2433 }
2434
2435 } // if a pin wasn't selected
2436
2437 } else {
2438 if (!m_showContextMenu) {
2439 if (AnnotationIsHovered()) {
2440 m_needsRedraw = true;
2441 AnnotationWasHovered = true;
2442 } else {
2443 AnnotationWasHovered = false;
2444 m_needsRedraw = true;
2445 }
2446 }
2447 }
2448
2449 m_draggingLastFrame = false;
2450 }
2451
2452 // Zoom:
2453 float mwheel = io.MouseWheel;
2454 if (mwheel != 0.0f) {
2455 mwheel *= zoomFactor;
2456
2457 Zoom(io.MousePos.x, io.MousePos.y, mwheel);
2458 }
2459 }
2460 }
2461
2462 if ((!io.WantCaptureKeyboard) && !io.KeyCtrl) {
2463
2464 if (ImGui::IsKeyPressed(SDLK_m)) {
2465 Mirror();
2466 CenterView();
2467 m_needsRedraw = true;
2468
2469 } else if (ImGui::IsKeyPressed(KM(SDL_SCANCODE_KP_PERIOD)) || ImGui::IsKeyPressed(SDLK_r) ||
2470 ImGui::IsKeyPressed(SDLK_PERIOD)) {
2471 // Rotate board: R and period rotate clockwise; comma rotates
2472 // counter-clockwise
2473 Rotate(1);
2474
2475 } else if (ImGui::IsKeyPressed(KM(SDL_SCANCODE_KP_0)) || ImGui::IsKeyPressed(SDLK_COMMA)) {
2476 Rotate(-1);
2477
2478 } else if (ImGui::IsKeyPressed(KM(SDL_SCANCODE_KP_PLUS)) || ImGui::IsKeyPressed(SDLK_EQUALS)) {
2479 Zoom(m_board_surface.x / 2, m_board_surface.y / 2, zoomFactor);
2480
2481 } else if (ImGui::IsKeyPressed(KM(SDL_SCANCODE_KP_MINUS)) || ImGui::IsKeyPressed(SDLK_MINUS)) {
2482 Zoom(m_board_surface.x / 2, m_board_surface.y / 2, -zoomFactor);
2483
2484 } else if (ImGui::IsKeyPressed(KM(SDL_SCANCODE_KP_2)) || ImGui::IsKeyPressed(SDLK_s)) {
2485 Pan(DIR_DOWN, panFactor);
2486
2487 } else if (ImGui::IsKeyPressed(KM(SDL_SCANCODE_KP_8)) || ImGui::IsKeyPressed(SDLK_w)) {
2488 Pan(DIR_UP, panFactor);
2489
2490 } else if (ImGui::IsKeyPressed(KM(SDL_SCANCODE_KP_4)) || ImGui::IsKeyPressed(SDLK_a)) {
2491 Pan(DIR_LEFT, panFactor);
2492
2493 } else if (ImGui::IsKeyPressed(KM(SDL_SCANCODE_KP_6)) || ImGui::IsKeyPressed(SDLK_d)) {
2494 Pan(DIR_RIGHT, panFactor);
2495
2496 } else if (ImGui::IsKeyPressed(KM(SDL_SCANCODE_KP_5)) || ImGui::IsKeyPressed(SDLK_x)) {
2497 // Center and reset zoom
2498 CenterView();
2499
2500 } else if (ImGui::IsKeyPressed(SDLK_c)) {
2501 if (io.KeyCtrl) {
2502 // quit OFBV
2503 m_wantsQuit = true;
2504 }
2505
2506 } else if (ImGui::IsKeyPressed(SDLK_i)) {
2507 showInfoPanel = !showInfoPanel;
2508 obvconfig.WriteBool("showInfoPanel", showInfoPanel ? true : false);
2509
2510 } else if (ImGui::IsKeyPressed(SDLK_l)) {
2511 // Show Net List
2512 m_showNetList = m_showNetList ? false : true;
2513
2514 } else if (ImGui::IsKeyPressed(SDLK_k)) {
2515 // Show Part List
2516 m_showPartList = m_showPartList ? false : true;
2517
2518 } else if (ImGui::IsKeyPressed(SDLK_SPACE)) {
2519 // Flip board:
2520 FlipBoard();
2521
2522 } else if (ImGui::IsKeyPressed(SDLK_p)) {
2523 showPins ^= 1;
2524 m_needsRedraw = true;
2525
2526 } else if (ImGui::IsKeyPressed(SDLK_f)) {
2527 if (io.KeyCtrl) {
2528 if (m_validBoard) {
2529 m_showSearch = true;
2530 m_needsRedraw = true;
2531 }
2532 }
2533
2534 } else if (ImGui::IsKeyPressed(SDLK_SLASH)) {
2535 if (m_validBoard) {
2536 m_showSearch = true;
2537 m_needsRedraw = true;
2538 }
2539
2540 } else if (ImGui::IsKeyPressed(SDLK_ESCAPE)) {
2541 ClearAllHighlights();
2542
2543 } else {
2544 /*
2545 * Do what ever is required for unhandled key presses.
2546 */
2547 }
2548 }
2549}
2550
2551/* END UPDATE REGION */
2552
2553/** Overlay and Windows
2554 *
2555 *
2556 */
2557void BoardView::ShowNetList(bool *p_open) {
2558 static NetList netList(bind(&BoardView::FindNet, this, _1));
2559 netList.Draw("Net List", p_open, m_board);
2560}
2561
2562void BoardView::ShowPartList(bool *p_open) {
2563 static PartList partList(bind(&BoardView::FindComponent, this, _1));
2564 partList.Draw("Part List", p_open, m_board);
2565}
2566
2567void BoardView::RenderOverlay() {
2568
2569 ShowInfoPane();
2570
2571 // Listing of Net elements
2572 if (m_showNetList) {
2573 ShowNetList(&m_showNetList);
2574 }
2575 if (m_showPartList) {
2576 ShowPartList(&m_showPartList);
2577 }
2578}
2579
2580/** End overlay & windows region **/
2581
2582/**
2583 * Drawing region
2584 *
2585 *
2586 *
2587 */
2588
2589void BoardView::CenterZoomNet(string netname) {
2590 ImVec2 view = m_board_surface;
2591 ImVec2 min, max;
2592 int i = 0;
2593
2594 if (!infoPanelCenterZoomNets) return;
2595
2596 min.x = min.y = FLT_MAX;
2597 max.x = max.y = FLT_MIN;
2598
2599 for (auto &pin : m_board->Pins()) {
2600 if (pin->net->name == netname) {
2601 auto p = pin->position;
2602 if (p.x < min.x) min.x = p.x;
2603 if (p.y < min.y) min.y = p.y;
2604 if (p.x > max.x) max.x = p.x;
2605 if (p.y > max.y) max.y = p.y;
2606
2607 if ((infoPanelSelectPartsOnNet) && (pin->type != Pin::kPinTypeTestPad)) {
2608 if (!contains(pin->component, m_partHighlighted)) {
2609 pin->component->visualmode = pin->component->CVMSelected;
2610 m_partHighlighted.push_back(pin->component);
2611 }
2612 }
2613 }
2614 }
2615
2616 // Bounds check!
2617 if ((min.x == FLT_MAX) || (min.y == FLT_MAX) || (max.x == FLT_MIN) || (max.y == FLT_MIN)) return;
2618
2619 if (debug) fprintf(stderr, "CenterzoomNet: bbox[%d]: %0.1f %0.1f - %0.1f %0.1f\n", i, min.x, min.y, max.x, max.y);
2620
2621 float dx = (max.x - min.x);
2622 float dy = (max.y - min.y);
2623 float sx = dx > 0 ? view.x / dx : FLT_MAX;
2624 float sy = dy > 0 ? view.y / dy : FLT_MAX;
2625
2626 m_scale = sx < sy ? sx : sy;
2627 if (m_scale == FLT_MAX) m_scale = m_scale_floor;
2628 m_scale /= partZoomScaleOutFactor;
2629 if (m_scale < m_scale_floor) m_scale = m_scale_floor;
2630
2631 /*
2632 float dx = (max.x - min.x);
2633 float dy = (max.y - min.y);
2634 float sx = dx > 0 ? view.x / dx : 1.0f;
2635 float sy = dy > 0 ? view.y / dy : 1.0f;
2636
2637 // m_rotation = 0;
2638 m_scale = sx < sy ? sx : sy;
2639 m_scale /= partZoomScaleOutFactor;
2640 if (m_scale < m_scale_floor) m_scale = m_scale_floor;
2641 */
2642
2643 m_dx = (max.x - min.x) / 2 + min.x;
2644 m_dy = (max.y - min.y) / 2 + min.y;
2645 SetTarget(m_dx, m_dy);
2646 m_needsRedraw = true;
2647}
2648
2649void BoardView::CenterZoomSearchResults(void) {
2650 // ImVec2 view = ImGui::GetIO().DisplaySize;
2651 ImVec2 view = m_board_surface;
2652 ImVec2 min, max;
2653 int i = 0;
2654
2655 if (!showPins) showPins = true; // Louis Rossmann UI failure fix.
2656
2657 if (!m_centerZoomSearchResults) return;
2658
2659 min.x = min.y = FLT_MAX;
2660 max.x = max.y = FLT_MIN;
2661
2662 for (auto &pp : m_pinHighlighted) {
2663 auto &p = pp->position;
2664 if (p.x < min.x) min.x = p.x;
2665 if (p.y < min.y) min.y = p.y;
2666 if (p.x > max.x) max.x = p.x;
2667 if (p.y > max.y) max.y = p.y;
2668 i++;
2669 }
2670
2671 for (auto &pp : m_partHighlighted) {
2672 for (auto &pn : pp->pins) {
2673 auto &p = pn->position;
2674 if (p.x < min.x) min.x = p.x;
2675 if (p.y < min.y) min.y = p.y;
2676 if (p.x > max.x) max.x = p.x;
2677 if (p.y > max.y) max.y = p.y;
2678 i++;
2679 }
2680 }
2681
2682 // Bounds check!
2683 if ((min.x == FLT_MAX) || (min.y == FLT_MAX) || (max.x == FLT_MIN) || (max.y == FLT_MIN)) return;
2684
2685 if (debug) fprintf(stderr, "CenterzoomSearchResults: bbox[%d]: %0.1f %0.1f - %0.1f %0.1f\n", i, min.x, min.y, max.x, max.y);
2686 // fprintf(stderr, "CenterzoomSearchResults: bbox[%d]: %0.1f %0.1f - %0.1f %0.1f\n", i, min.x, min.y, max.x, max.y);
2687
2688 float dx = (max.x - min.x);
2689 float dy = (max.y - min.y);
2690 float sx = dx > 0 ? view.x / dx : FLT_MAX;
2691 float sy = dy > 0 ? view.y / dy : FLT_MAX;
2692
2693 m_scale = sx < sy ? sx : sy;
2694 if (m_scale == FLT_MAX) m_scale = m_scale_floor;
2695 m_scale /= partZoomScaleOutFactor;
2696 if (m_scale < m_scale_floor) m_scale = m_scale_floor;
2697
2698 m_dx = (max.x - min.x) / 2 + min.x;
2699 m_dy = (max.y - min.y) / 2 + min.y;
2700 SetTarget(m_dx, m_dy);
2701 m_needsRedraw = true;
2702}
2703
2704/*
2705 * EPC = External Pin Count; finds pins which are not contained within
2706 * the outline and flips the board outline if required, as it seems some
2707 * brd2 files are coming with a y-flipped outline
2708 */
2709int BoardView::EPCCheck(void) {
2710 int epc[2] = {0, 0};
2711 int side;
2712 auto &outline = m_board->OutlinePoints();
2713 ImVec2 min, max;
2714
2715 // find the orthagonal bounding box
2716 // probably can put this as a predefined
2717 min.x = min.y = FLT_MAX;
2718 max.x = max.y = FLT_MIN;
2719 for (auto &p : outline) {
2720 if (p->x < min.x) min.x = p->x;
2721 if (p->y < min.y) min.y = p->y;
2722 if (p->x > max.x) max.x = p->x;
2723 if (p->y > max.y) max.y = p->y;
2724 }
2725
2726 for (side = 0; side < 2; side++) {
2727 for (auto &p : m_board->Pins()) {
2728 // auto p = pin.get();
2729 int l, r;
2730 int jump = 1;
2731 Point fp;
2732
2733 l = 0;
2734 r = 0;
2735
2736 for (size_t i = 0; i < outline.size() - 1; i++) {
2737 Point &pa = *outline[i];
2738 Point &pb = *outline[i + 1];
2739
2740 // jump double/dud points
2741 if (pa.x == pb.x && pa.y == pb.y) continue;
2742
2743 // if we encounter our hull/poly start point, then we've now created the
2744 // closed
2745 // hull, jump the next segment and reset the first-point
2746 if ((!jump) && (fp.x == pb.x) && (fp.y == pb.y)) {
2747 if (i < outline.size() - 2) {
2748 fp = *outline[i + 2];
2749 jump = 1;
2750 i++;
2751 }
2752 } else {
2753 jump = 0;
2754 }
2755
2756 // test to see if this segment makes the scan-cut.
2757 if ((pa.y > pb.y && p->position.y < pa.y && p->position.y > pb.y) ||
2758 (pa.y < pb.y && p->position.y > pa.y && p->position.y < pb.y)) {
2759 ImVec2 intersect;
2760
2761 intersect.y = p->position.y;
2762 if (pa.x == pb.x)
2763 intersect.x = pa.x;
2764 else
2765 intersect.x = (pb.x - pa.x) / (pb.y - pa.y) * (p->position.y - pa.y) + pa.x;
2766
2767 if (intersect.x > p->position.x)
2768 r++;
2769 else if (intersect.x < p->position.x)
2770 l++;
2771 }
2772 } // if we did get an intersection
2773
2774 // If either side has no intersections, then it's out of bounds (likely)
2775 if ((l % 2 == 0) && (r % 2 == 0)) epc[side]++;
2776 } // pins
2777
2778 if (debug) fprintf(stderr, "EPC[%d]: %d\n", side, epc[side]);
2779
2780 // flip the outline
2781 for (auto &p : outline) p->y = max.y - p->y;
2782
2783 } // side
2784
2785 if ((epc[0] || epc[1]) && (epc[0] > epc[1])) {
2786 for (auto &p : outline) p->y = max.y - p->y;
2787 }
2788
2789 return 0;
2790}
2791
2792/*
2793 * Experimenting to see how much CPU hit rescanning and
2794 * drawing the flood fill is (as pin-stripe) each time
2795 * as opposed to precalculated line list
2796 *
2797 * We also do this slightly differently, we ask for a
2798 * y-pixel delta and thickness of line
2799 *
2800 */
2801void BoardView::OutlineGenFillDraw(ImDrawList *draw, int ydelta, double thickness = 1.0f) {
2802
2803 auto io = ImGui::GetIO();
2804 vector<ImVec2> scanhits;
2805 auto &outline = m_board->OutlinePoints();
2806 static ImVec2 min, max; // board min/max points
2807 // double steps = 500;
2808 double vdelta;
2809 double y, ystart, yend;
2810
2811 if (!boardFill) return;
2812 if (!m_file) return;
2813
2814 scanhits.reserve(20);
2815
2816 draw->ChannelsSetCurrent(kChannelFill);
2817
2818 // find the orthagonal bounding box
2819 // probably can put this as a predefined
2820 if (!boardMinMaxDone) {
2821 min.x = min.y = 100000000000;
2822 max.x = max.y = -100000000000;
2823 for (auto &p : outline) {
2824 if (p->x < min.x) min.x = p->x;
2825 if (p->y < min.y) min.y = p->y;
2826 if (p->x > max.x) max.x = p->x;
2827 if (p->y > max.y) max.y = p->y;
2828 }
2829 boardMinMaxDone = true;
2830 }
2831
2832 // Get the viewport limits, so we don't waste time scanning what we don't need
2833 ImVec2 vpa = ScreenToCoord(0, 0);
2834 ImVec2 vpb = ScreenToCoord(io.DisplaySize.x, io.DisplaySize.y);
2835
2836 if (vpa.y > vpb.y) {
2837 ystart = vpb.y;
2838 yend = vpa.y;
2839 } else {
2840 ystart = vpa.y;
2841 yend = vpb.y;
2842 }
2843
2844 if (ystart < min.y) ystart = min.y;
2845 if (yend > max.y) yend = max.y;
2846
2847 // vdelta = (ystart - yend) / steps;
2848
2849 vdelta = ydelta / m_scale;
2850
2851 /*
2852 * Go through each scan line
2853 */
2854 y = min.y;
2855 while (y < max.y) {
2856
2857 scanhits.resize(0);
2858 // scan outline segments to see if any intersect with our horizontal scan line
2859
2860 /*
2861 * While we haven't yet exhausted possible scan hits
2862 */
2863
2864 {
2865
2866 int jump = 1;
2867 Point fp;
2868
2869 auto &outline = m_board->OutlinePoints();
2870
2871 // set our initial draw point, so we can detect when we encounter it again
2872 fp = *outline[0];
2873
2874 for (size_t i = 0; i < outline.size() - 1; i++) {
2875 Point &pa = *outline[i];
2876 Point &pb = *outline[i + 1];
2877
2878 // jump double/dud points
2879 if (pa.x == pb.x && pa.y == pb.y) continue;
2880
2881 // if we encounter our hull/poly start point, then we've now created the
2882 // closed
2883 // hull, jump the next segment and reset the first-point
2884 if ((!jump) && (fp.x == pb.x) && (fp.y == pb.y)) {
2885 if (i < outline.size() - 2) {
2886 fp = *outline[i + 2];
2887 jump = 1;
2888 i++;
2889 }
2890 } else {
2891 jump = 0;
2892 }
2893
2894 // test to see if this segment makes the scan-cut.
2895 if ((pa.y > pb.y && y < pa.y && y > pb.y) || (pa.y < pb.y && y > pa.y && y < pb.y)) {
2896 ImVec2 intersect;
2897
2898 intersect.y = y;
2899 if (pa.x == pb.x)
2900 intersect.x = pa.x;
2901 else
2902 intersect.x = (pb.x - pa.x) / (pb.y - pa.y) * (y - pa.y) + pa.x;
2903 scanhits.push_back(intersect);
2904 }
2905 } // if we did get an intersection
2906
2907 sort(scanhits.begin(), scanhits.end(), [](ImVec2 const &a, ImVec2 const &b) { return a.x < b.x; });
2908
2909 // now finally generate the lines.
2910 {
2911 int i = 0;
2912 int l = scanhits.size() - 1;
2913 for (i = 0; i < l; i += 2) {
2914 draw->AddLine(
2915 CoordToScreen(scanhits[i].x, y), CoordToScreen(scanhits[i + 1].x, y), m_colors.boardFillColor, thickness);
2916 }
2917 }
2918 }
2919 y += vdelta;
2920 } // for each scan line
2921}
2922
2923void BoardView::DrawDiamond(ImDrawList *draw, ImVec2 c, double r, uint32_t color) {
2924 ImVec2 dia[4];
2925
2926 dia[0] = ImVec2(c.x, c.y - r);
2927 dia[1] = ImVec2(c.x + r, c.y);
2928 dia[2] = ImVec2(c.x, c.y + r);
2929 dia[3] = ImVec2(c.x - r, c.y);
2930
2931 draw->AddPolyline(dia, 4, color, true, 1.0f, true);
2932}
2933
2934void BoardView::DrawHex(ImDrawList *draw, ImVec2 c, double r, uint32_t color) {
2935 double da, db;
2936 ImVec2 hex[6];
2937
2938 da = r * 0.5; // cos(60')
2939 db = r * 0.866025404; // sin(60')
2940
2941 hex[0] = ImVec2(c.x - r, c.y);
2942 hex[1] = ImVec2(c.x - da, c.y - db);
2943 hex[2] = ImVec2(c.x + da, c.y - db);
2944 hex[3] = ImVec2(c.x + r, c.y);
2945 hex[4] = ImVec2(c.x + da, c.y + db);
2946 hex[5] = ImVec2(c.x - da, c.y + db);
2947
2948 draw->AddPolyline(hex, 6, color, true, 1.0f, true);
2949}
2950
2951inline void BoardView::DrawOutline(ImDrawList *draw) {
2952 int jump = 1;
2953 Point fp;
2954
2955 auto &outline = m_board->OutlinePoints();
2956
2957 draw->ChannelsSetCurrent(kChannelPolylines);
2958
2959 // set our initial draw point, so we can detect when we encounter it again
2960 fp = *outline[0];
2961
2962 draw->PathClear();
2963 for (size_t i = 0; i < outline.size() - 1; i++) {
2964 Point &pa = *outline[i];
2965 Point &pb = *outline[i + 1];
2966
2967 // jump double/dud points
2968 if (pa.x == pb.x && pa.y == pb.y) continue;
2969
2970 // if we encounter our hull/poly start point, then we've now created the
2971 // closed
2972 // hull, jump the next segment and reset the first-point
2973 if ((!jump) && (fp.x == pb.x) && (fp.y == pb.y)) {
2974 if (i < outline.size() - 2) {
2975 fp = *outline[i + 2];
2976 jump = 1;
2977 i++;
2978 }
2979 } else {
2980 jump = 0;
2981 }
2982
2983 ImVec2 spa = CoordToScreen(pa.x, pa.y);
2984 ImVec2 spb = CoordToScreen(pb.x, pb.y);
2985
2986 /*
2987 * If we have a pin selected, we mask off the colour to shade out
2988 * things and make it easier to see associated pins/points
2989 */
2990 if ((pinSelectMasks) && (m_pinSelected || m_pinHighlighted.size())) {
2991 draw->AddLine(spa, spb, (m_colors.boardOutlineColor & m_colors.selectedMaskOutline) | m_colors.orMaskOutline);
2992 } else {
2993 draw->AddLine(spa, spb, m_colors.boardOutlineColor);
2994 }
2995 } // for
2996}
2997
2998void BoardView::DrawSelectedWeb(ImDrawList *draw) {
2999 if (!showNetWeb) return;
3000
3001 bool first = true;
3002 Point fp;
3003 /*
3004 * Some nets we don't bother showing, because they're not relevant or
3005 * produce too many results (such as ground!)
3006 */
3007 for (auto &p : m_pinHighlighted) {
3008 if (first) {
3009 fp = p->position;
3010 first = false;
3011 }
3012 uint32_t col = m_colors.pinNetWebColor;
3013 if (!ComponentIsVisible(p->component)) {
3014 col = m_colors.pinNetWebOSColor;
3015 draw->AddCircle(CoordToScreen(p->position.x, p->position.y), p->diameter * m_scale, col, 16);
3016 }
3017
3018 draw->AddLine(CoordToScreen(fp.x, fp.y), CoordToScreen(p->position.x, p->position.y), ImColor(col), 1);
3019 }
3020
3021 return;
3022}
3023
3024void BoardView::DrawNetWeb(ImDrawList *draw) {
3025 if (!showNetWeb) return;
3026
3027 /*
3028 * Some nets we don't bother showing, because they're not relevant or
3029 * produce too many results (such as ground!)
3030 */
3031 if (m_pinSelected->type == Pin::kPinTypeNotConnected) return;
3032 if (m_pinSelected->type == Pin::kPinTypeUnkown) return;
3033 if (m_pinSelected->net->is_ground) return;
3034
3035 for (auto &p : m_board->Pins()) {
3036
3037 if (p->net == m_pinSelected->net) {
3038 uint32_t col = m_colors.pinNetWebColor;
3039 if (!ComponentIsVisible(p->component)) {
3040 col = m_colors.pinNetWebOSColor;
3041 draw->AddCircle(CoordToScreen(p->position.x, p->position.y), p->diameter * m_scale, col, 16);
3042 }
3043
3044 draw->AddLine(CoordToScreen(m_pinSelected->position.x, m_pinSelected->position.y),
3045 CoordToScreen(p->position.x, p->position.y),
3046 ImColor(col),
3047 1);
3048 }
3049 }
3050
3051 return;
3052}
3053
3054inline void BoardView::DrawPins(ImDrawList *draw) {
3055
3056 uint32_t cmask = 0xFFFFFFFF;
3057 uint32_t omask = 0x00000000;
3058 float threshold = 0;
3059 auto io = ImGui::GetIO();
3060
3061 if (!showPins) return;
3062
3063 /*
3064 * If we have a pin selected, then it makes it
3065 * easier to see where the associated pins are
3066 * by masking out (alpha or channel) the other
3067 * pins so they're fainter.
3068 */
3069 if (pinSelectMasks) {
3070 if (m_pinSelected || m_pinHighlighted.size()) {
3071 cmask = m_colors.selectedMaskPins;
3072 omask = m_colors.orMaskPins;
3073 }
3074 }
3075
3076 if (slowCPU) {
3077 threshold = 2.0f;
3078 pinShapeSquare = true;
3079 pinShapeCircle = false;
3080 }
3081 if (pinSizeThresholdLow > threshold) threshold = pinSizeThresholdLow;
3082
3083 draw->ChannelsSetCurrent(kChannelPins);
3084
3085 if (m_pinSelected) DrawNetWeb(draw);
3086 // if (m_pinHighlighted.size()) DrawSelectedWeb(draw);
3087
3088 for (auto &pin : m_board->Pins()) {
3089 float psz = pin->diameter * m_scale;
3090 uint32_t fill_color = 0xFFFF8888; // fallback fill colour
3091 uint32_t text_color = m_colors.pinDefaultTextColor;
3092 uint32_t color = (m_colors.pinDefaultColor & cmask) | omask;
3093 bool fill_pin = false;
3094 bool show_text = false;
3095 bool draw_ring = true;
3096
3097 // continue if pin is not visible anyway
3098 if (!ComponentIsVisible(pin->component)) continue;
3099
3100 ImVec2 pos = CoordToScreen(pin->position.x, pin->position.y);
3101 {
3102 if (!IsVisibleScreen(pos.x, pos.y, psz, io)) continue;
3103 }
3104
3105 if ((!m_pinSelected) && (psz < threshold)) continue;
3106
3107 // color & text depending on app state & pin type
3108
3109 {
3110 /*
3111 * Pins resulting from a net search
3112 */
3113 if (contains(pin, m_pinHighlighted)) {
3114 if (psz < fontSize / 2) psz = fontSize / 2;
3115 text_color = m_colors.pinSelectedTextColor;
3116 fill_color = m_colors.pinSelectedFillColor;
3117 color = m_colors.pinSelectedColor;
3118 // text_color = color = m_colors.pinSameNetColor;
3119 fill_pin = true;
3120 show_text = true;
3121 draw_ring = true;
3122 threshold = 0;
3123 // draw->AddCircle(ImVec2(pos.x, pos.y), psz * pinHaloDiameter, ImColor(0xff0000ff), 32);
3124 }
3125
3126 if (!pin->net || pin->type == Pin::kPinTypeNotConnected) {
3127 color = (m_colors.pinNotConnectedColor & cmask) | omask;
3128 } else {
3129 if (pin->net->is_ground) color = (m_colors.pinGroundColor & cmask) | omask;
3130 }
3131
3132 /*
3133 * If the part is selected, as part of search or otherwise
3134 */
3135 if (PartIsHighlighted(pin->component)) {
3136 color = m_colors.pinDefaultColor;
3137 text_color = m_colors.pinDefaultTextColor;
3138 fill_pin = false;
3139 draw_ring = true;
3140 show_text = true;
3141 threshold = 0;
3142 }
3143
3144 if (pin->type == Pin::kPinTypeTestPad) {
3145 color = (m_colors.pinTestPadColor & cmask) | omask;
3146 fill_color = (m_colors.pinTestPadFillColor & cmask) | omask;
3147 show_text = false;
3148 }
3149
3150 // If the part itself is highlighted ( CVMShowPins )
3151 // if (p_pin->component->visualmode == p_pin->component->CVMSelected) {
3152 if (pin->component->visualmode == pin->component->CVMSelected) {
3153 color = m_colors.pinDefaultColor;
3154 text_color = m_colors.pinDefaultTextColor;
3155 fill_pin = false;
3156 draw_ring = true;
3157 show_text = true;
3158 threshold = 0;
3159 }
3160
3161 // pin is on the same net as selected pin: highlight > rest
3162 if (m_pinSelected && pin->net == m_pinSelected->net) {
3163 if (psz < fontSize / 2) psz = fontSize / 2;
3164 color = m_colors.pinSameNetColor;
3165 text_color = m_colors.pinSameNetTextColor;
3166 fill_color = m_colors.pinSameNetFillColor;
3167 draw_ring = false;
3168 fill_pin = true;
3169 show_text = true; // is this something we want? Maybe an optional thing?
3170 threshold = 0;
3171 }
3172
3173 // pin selected overwrites everything
3174 // if (p_pin == m_pinSelected) {
3175 if (pin == m_pinSelected) {
3176 if (psz < fontSize / 2) psz = fontSize / 2;
3177 color = m_colors.pinSelectedColor;
3178 text_color = m_colors.pinSelectedTextColor;
3179 fill_color = m_colors.pinSelectedFillColor;
3180 draw_ring = false;
3181 show_text = true;
3182 fill_pin = true;
3183 threshold = 0;
3184 }
3185
3186 // don't show text if it doesn't make sense
3187 if (pin->component->pins.size() <= 1) show_text = false;
3188 if (pin->type == Pin::kPinTypeTestPad) show_text = false;
3189 }
3190
3191 // Drawing
3192 {
3193 int segments;
3194 // draw->ChannelsSetCurrent(kChannelImages);
3195
3196 // for the round pin representations, choose how many circle segments need
3197 // based on the pin size
3198 segments = round(psz);
3199 if (segments > 32) segments = 32;
3200 if (segments < 8) segments = 8;
3201 float h = psz / 2 + 0.5f;
3202
3203 /*
3204 * if we're going to be showing the text of a pin, then we really
3205 * should make sure that the drawn pin is at least as big as a single
3206 * character so it doesn't look messy
3207 */
3208 if ((show_text) && (psz < fontSize / 2)) psz = fontSize / 2;
3209
3210 switch (pin->type) {
3211 case Pin::kPinTypeTestPad:
3212 if ((psz > 3) && (!slowCPU)) {
3213 draw->AddCircleFilled(ImVec2(pos.x, pos.y), psz, fill_color, segments);
3214 draw->AddCircle(ImVec2(pos.x, pos.y), psz, color, segments);
3215 } else if (psz > threshold) {
3216 draw->AddRectFilled(ImVec2(pos.x - h, pos.y - h), ImVec2(pos.x + h, pos.y + h), fill_color);
3217 }
3218 break;
3219 default:
3220 if ((psz > 3) && (psz > threshold)) {
3221 if (pinShapeSquare) {
3222 if (fill_pin)
3223 draw->AddRectFilled(ImVec2(pos.x - h, pos.y - h), ImVec2(pos.x + h, pos.y + h), fill_color);
3224 if (draw_ring) draw->AddRect(ImVec2(pos.x - h, pos.y - h), ImVec2(pos.x + h, pos.y + h), color);
3225 } else {
3226 if (fill_pin) draw->AddCircleFilled(ImVec2(pos.x, pos.y), psz, fill_color, segments);
3227 if (draw_ring) draw->AddCircle(ImVec2(pos.x, pos.y), psz, color, segments);
3228 }
3229 } else if (psz > threshold) {
3230 if (fill_pin) draw->AddRectFilled(ImVec2(pos.x - h, pos.y - h), ImVec2(pos.x + h, pos.y + h), fill_color);
3231 if (draw_ring) draw->AddRect(ImVec2(pos.x - h, pos.y - h), ImVec2(pos.x + h, pos.y + h), color);
3232 }
3233 }
3234
3235 // if (p_pin == m_pinSelected) {
3236 // if (pin.get() == m_pinSelected) {
3237 // draw->AddCircle(ImVec2(pos.x, pos.y), psz + 1.25, m_colors.pinSelectedTextColor, segments);
3238 // }
3239
3240 // if ((color == m_colors.pinSameNetColor) && (pinHalo == true)) {
3241 // draw->AddCircle(ImVec2(pos.x, pos.y), psz * pinHaloDiameter, m_colors.pinHaloColor, segments,
3242 // pinHaloThickness);
3243 // }
3244
3245 if (show_text) {
3246 const char *pin_number = pin->number.c_str();
3247
3248 ImVec2 text_size = ImGui::CalcTextSize(pin_number);
3249 ImVec2 pos_adj = ImVec2(pos.x - text_size.x * 0.5f, pos.y - text_size.y * 0.5f);
3250
3251 draw->ChannelsSetCurrent(kChannelText);
3252 draw->AddText(pos_adj, text_color, pin_number);
3253 draw->ChannelsSetCurrent(kChannelPins);
3254 }
3255 }
3256 }
3257}
3258
3259inline void BoardView::DrawParts(ImDrawList *draw) {
3260 // float psz = (float)m_pinDiameter * 0.5f * m_scale;
3261 double angle;
3262 double distance = 0;
3263 struct ImVec2 pva[2000], *ppp;
3264 uint32_t color = m_colors.partOutlineColor;
3265 // int rendered = 0;
3266 char p0, p1; // first two characters of the part name, code-writing
3267 // convenience more than anything else
3268
3269 draw->ChannelsSetCurrent(kChannelPolylines);
3270 /*
3271 * If a pin has been selected, we mask out the colour to
3272 * enhance (relatively) the appearance of the pin(s)
3273 */
3274 if (pinSelectMasks && ((m_pinSelected) || m_pinHighlighted.size())) {
3275 color = (m_colors.partOutlineColor & m_colors.selectedMaskParts) | m_colors.orMaskParts;
3276 }
3277
3278 for (auto &part : m_board->Components()) {
3279 int pincount = 0;
3280 double min_x, min_y, max_x, max_y, aspect;
3281 outline_pt dbox[4]; // default box, if there's nothing else claiming to render the part different.
3282
3283 if (part->is_dummy()) continue;
3284
3285 /*
3286 *
3287 * When we first load the board, the outline of each part isn't (yet) rendered
3288 * or determined/calculated. So, the first time we display the board we
3289 * compute the outline and store it for later.
3290 *
3291 * This also sets the pin diameters too.
3292 *
3293 */
3294 if (!part->outline_done) { // should only need to do this once for most parts
3295
3296 ppp = &pva[0];
3297 if (part->pins.size() == 0) {
3298 if (debug) fprintf(stderr, "WARNING: Drawing empty part %s\n", part->name.c_str());
3299 draw->AddRect(CoordToScreen(part->p1.x + DPIF(10), part->p1.y + DPIF(10)),
3300 CoordToScreen(part->p2.x - DPIF(10), part->p2.y - DPIF(10)),
3301 0xff0000ff);
3302 draw->AddText(
3303 CoordToScreen(part->p1.x + DPIF(10), part->p1.y - DPIF(50)), m_colors.partTextColor, part->name.c_str());
3304 continue;
3305 }
3306
3307 for (auto &pin : part->pins) {
3308 pincount++;
3309
3310 // scale box around pins as a fallback, else either use polygon or convex
3311 // hull for better shape fidelity
3312 if (pincount == 1) {
3313 min_x = pin->position.x;
3314 min_y = pin->position.y;
3315 max_x = min_x;
3316 max_y = min_y;
3317 }
3318
3319 if (pincount < 2000) {
3320 ppp->x = pin->position.x;
3321 ppp->y = pin->position.y;
3322 ppp++;
3323 }
3324
3325 if (pin->position.x > max_x) {
3326 max_x = pin->position.x;
3327
3328 } else if (pin->position.x < min_x) {
3329 min_x = pin->position.x;
3330 }
3331 if (pin->position.y > max_y) {
3332 max_y = pin->position.y;
3333
3334 } else if (pin->position.y < min_y) {
3335 min_y = pin->position.y;
3336 }
3337 }
3338
3339 part->omin = ImVec2(min_x, min_y);
3340 part->omax = ImVec2(max_x, max_y);
3341 part->centerpoint = ImVec2((max_x - min_x) / 2 + min_x, (max_y - min_y) / 2 + min_y);
3342
3343 distance = sqrt((max_x - min_x) * (max_x - min_x) + (max_y - min_y) * (max_y - min_y));
3344
3345 float pin_radius = m_pinDiameter / 2.0f;
3346
3347 /*
3348 *
3349 * Determine the size of our part's pin radius based on the distance
3350 * between the extremes of the pin coordinates.
3351 *
3352 * All the figures below are determined empirically rather than any
3353 * specific formula.
3354 *
3355 */
3356 if ((pincount < 4) && (part->name[0] != 'U') && (part->name[0] != 'Q')) {
3357
3358 if ((distance > 52) && (distance < 57)) {
3359 // 0603
3360 pin_radius = 15;
3361 for (auto &pin : part->pins) {
3362 pin->diameter = pin_radius; // * 0.05;
3363 }
3364
3365 } else if ((distance > 247) && (distance < 253)) {
3366 // SMC diode?
3367 pin_radius = 50;
3368 for (auto &pin : part->pins) {
3369 pin->diameter = pin_radius; // * 0.05;
3370 }
3371
3372 } else if ((distance > 195) && (distance < 199)) {
3373 // Inductor?
3374 pin_radius = 50;
3375 for (auto &pin : part->pins) {
3376 pin->diameter = pin_radius; // * 0.05;
3377 }
3378
3379 } else if ((distance > 165) && (distance < 169)) {
3380 // SMB diode?
3381 pin_radius = 35;
3382 for (auto &pin : part->pins) {
3383 pin->diameter = pin_radius; // * 0.05;
3384 }
3385
3386 } else if ((distance > 101) && (distance < 109)) {
3387 // SMA diode / tant cap
3388 pin_radius = 30;
3389 for (auto &pin : part->pins) {
3390 pin->diameter = pin_radius; // * 0.05;
3391 }
3392
3393 } else if ((distance > 108) && (distance < 112)) {
3394 // 1206
3395 pin_radius = 30;
3396 for (auto &pin : part->pins) {
3397 pin->diameter = pin_radius; // * 0.05;
3398 }
3399
3400 } else if ((distance > 64) && (distance < 68)) {
3401 // 0805
3402 pin_radius = 25;
3403 for (auto &pin : part->pins) {
3404 pin->diameter = pin_radius; // * 0.05;
3405 }
3406
3407 } else if ((distance > 18) && (distance < 22)) {
3408 // 0201 cap/resistor?
3409 pin_radius = 5;
3410 for (auto &pin : part->pins) {
3411 pin->diameter = pin_radius; // * 0.05;
3412 }
3413 } else if ((distance > 28) && (distance < 32)) {
3414 // 0402 cap/resistor
3415 pin_radius = 10;
3416 for (auto &pin : part->pins) {
3417 pin->diameter = pin_radius; // * 0.05;
3418 }
3419 }
3420 }
3421
3422 // TODO: pin radius is stored in Pin object
3423 //
3424 //
3425 //
3426 min_x -= pin_radius;
3427 max_x += pin_radius;
3428 min_y -= pin_radius;
3429 max_y += pin_radius;
3430
3431 if ((max_y - min_y) < 0.01)
3432 aspect = 0;
3433 else
3434 aspect = (max_x - min_x) / (max_y - min_y);
3435
3436 dbox[0].x = dbox[3].x = min_x;
3437 dbox[1].x = dbox[2].x = max_x;
3438 dbox[0].y = dbox[1].y = min_y;
3439 dbox[3].y = dbox[2].y = max_y;
3440
3441 p0 = part->name[0];
3442 p1 = part->name[1];
3443
3444 /*
3445 * Draw all 2~3 pin devices as if they're not orthagonal. It's a bit more
3446 * CPU
3447 * overhead but it keeps the code simpler and saves us replicating things.
3448 */
3449
3450 if ((pincount == 3) && (abs(aspect > 0.5)) &&
3451 ((strchr("DQZ", p0) || (strchr("DQZ", p1)) || strcmp(part->name.c_str(), "LED")))) {
3452 outline_pt *hpt;
3453
3454 memcpy(part->outline, dbox, sizeof(dbox));
3455 part->outline_done = true;
3456
3457 hpt = part->hull = (outline_pt *)malloc(sizeof(outline_pt) * 3);
3458 for (auto &pin : part->pins) {
3459 hpt->x = pin->position.x;
3460 hpt->y = pin->position.y;
3461 hpt++;
3462 }
3463 part->hull_count = 3;
3464
3465 /*
3466 * handle all other devices not specifically handled above
3467 */
3468 } else if ((pincount > 1) && (pincount < 4) && ((strchr("CRLD", p0) || (strchr("CRLD", p1))))) {
3469 double dx, dy;
3470 double tx, ty;
3471 double armx, army;
3472
3473 dx = part->pins[1]->position.x - part->pins[0]->position.x;
3474 dy = part->pins[1]->position.y - part->pins[0]->position.y;
3475 angle = atan2(dy, dx);
3476
3477 if (((p0 == 'L') || (p1 == 'L')) && (distance > 50)) {
3478 pin_radius = 15;
3479 for (auto &pin : part->pins) {
3480 pin->diameter = pin_radius; // * 0.05;
3481 }
3482 army = distance / 2;
3483 armx = pin_radius;
3484 } else if (((p0 == 'C') || (p1 == 'C')) && (distance > 90)) {
3485 double mpx, mpy;
3486
3487 pin_radius = 15;
3488 for (auto &pin : part->pins) {
3489 pin->diameter = pin_radius; // * 0.05;
3490 }
3491 army = distance / 2 - distance / 4;
3492 armx = pin_radius;
3493
3494 mpx = dx / 2 + part->pins[0]->position.x;
3495 mpy = dy / 2 + part->pins[0]->position.y;
3496 VHRotateV(&mpx, &mpy, dx / 2 + part->pins[0]->position.x, dy / 2 + part->pins[0]->position.y, angle);
3497
3498 part->expanse = distance;
3499 part->centerpoint.x = mpx;
3500 part->centerpoint.y = mpy;
3501 part->component_type = part->kComponentTypeCapacitor;
3502
3503 } else {
3504 armx = army = pin_radius;
3505 }
3506
3507 // TODO: Compact this bit of code, maybe. It works at least.
3508 tx = part->pins[0]->position.x - armx;
3509 ty = part->pins[0]->position.y - army;
3510 VHRotateV(&tx, &ty, part->pins[0]->position.x, part->pins[0]->position.y, angle);
3511 // a = CoordToScreen(tx, ty);
3512 part->outline[0].x = tx;
3513 part->outline[0].y = ty;
3514
3515 tx = part->pins[0]->position.x - armx;
3516 ty = part->pins[0]->position.y + army;
3517 VHRotateV(&tx, &ty, part->pins[0]->position.x, part->pins[0]->position.y, angle);
3518 // b = CoordToScreen(tx, ty);
3519 part->outline[1].x = tx;
3520 part->outline[1].y = ty;
3521
3522 tx = part->pins[1]->position.x + armx;
3523 ty = part->pins[1]->position.y + army;
3524 VHRotateV(&tx, &ty, part->pins[1]->position.x, part->pins[1]->position.y, angle);
3525 // c = CoordToScreen(tx, ty);
3526 part->outline[2].x = tx;
3527 part->outline[2].y = ty;
3528
3529 tx = part->pins[1]->position.x + armx;
3530 ty = part->pins[1]->position.y - army;
3531 VHRotateV(&tx, &ty, part->pins[1]->position.x, part->pins[1]->position.y, angle);
3532 // d = CoordToScreen(tx, ty);
3533 part->outline[3].x = tx;
3534 part->outline[3].y = ty;
3535
3536 part->outline_done = true;
3537
3538 // rendered = 1;
3539
3540 } else {
3541
3542 /*
3543 * If we have (typically) a connector with a non uniform pin distribution
3544 * then we can try use the minimal bounding box algorithm
3545 * to give it a more sane outline
3546 */
3547 if ((pincount >= 4) && ((strchr("UJL", p0) || strchr("UJL", p1) || (strncmp(part->name.c_str(), "CN", 2) == 0)))) {
3548 ImVec2 *hull;
3549 int hpc;
3550
3551 hull = (ImVec2 *)malloc(sizeof(ImVec2) * pincount); // massive overkill since our hull will
3552 // only require the perimeter points
3553 if (hull) {
3554 // Find our hull
3555 hpc = VHConvexHull(hull, pva, pincount); // (hpc = hull pin count)
3556
3557 // If we had a valid hull, then find the MBB for it
3558 if (hpc > 0) {
3559 int i;
3560 ImVec2 bbox[4];
3561 outline_pt *hpt;
3562 part->hull_count = hpc;
3563
3564 /*
3565 * compute the convex hull and then transfer
3566 * points to the part
3567 */
3568 hpt = part->hull = (outline_pt *)malloc(sizeof(outline_pt) * (hpc + 1));
3569 for (i = 0; i < hpc; i++) {
3570 hpt->x = hull[i].x;
3571 hpt->y = hull[i].y;
3572 hpt++;
3573 }
3574
3575 VHMBBCalculate(bbox, hull, hpc, pin_radius);
3576 for (i = 0; i < 4; i++) {
3577 part->outline[i].x = bbox[i].x;
3578 part->outline[i].y = bbox[i].y;
3579 }
3580
3581 part->outline_done = true;
3582
3583 /*
3584 * Tighten the hull, removes any small angle segments
3585 * such as a sequence of pins in a line, might be an overkill
3586 */
3587 // hpc = TightenHull(hull, hpc, 0.1f);
3588 }
3589
3590 free(hull);
3591 } else {
3592 fprintf(stderr, "ERROR: Cannot allocate memory for convex hull generation (%s)", strerror(errno));
3593 memcpy(part->outline, dbox, sizeof(dbox));
3594 part->outline_done = true;
3595 // draw->AddRect(min, max, color);
3596 // rendered = 1;
3597 }
3598
3599 } else {
3600 // if it wasn't at an odd angle, or wasn't large, or wasn't a connector,
3601 // just an ordinary
3602 // type part, then this is where we'll likely end up
3603 memcpy(part->outline, dbox, sizeof(dbox));
3604 part->outline_done = true;
3605 }
3606 }
3607
3608 // if (rendered == 0) {
3609 // fprintf(stderr, "Part wasn't rendered (%s)\n", part->name.c_str());
3610 // }
3611
3612 } // if !outline_done
3613
3614 if (!ComponentIsVisible(part)) continue;
3615
3616 if (part->outline_done) {
3617
3618 /*
3619 * Draw the bounding box for the part
3620 */
3621 ImVec2 a, b, c, d;
3622
3623 a = ImVec2(CoordToScreen(part->outline[0].x, part->outline[0].y));
3624 b = ImVec2(CoordToScreen(part->outline[1].x, part->outline[1].y));
3625 c = ImVec2(CoordToScreen(part->outline[2].x, part->outline[2].y));
3626 d = ImVec2(CoordToScreen(part->outline[3].x, part->outline[3].y));
3627
3628 // if (fillParts) draw->AddQuadFilled(a, b, c, d, color & 0xffeeeeee);
3629 if (fillParts) draw->AddQuadFilled(a, b, c, d, m_colors.partFillColor);
3630 draw->AddQuad(a, b, c, d, color);
3631 if (PartIsHighlighted(part)) {
3632 if (fillParts) draw->AddQuadFilled(a, b, c, d, m_colors.partHighlightedFillColor);
3633 draw->AddQuad(a, b, c, d, m_colors.partHighlightedColor);
3634 }
3635
3636 /*
3637 * Draw the convex hull of the part if it has one
3638 */
3639 if (part->hull) {
3640 int i;
3641 draw->PathClear();
3642 for (i = 0; i < part->hull_count; i++) {
3643 ImVec2 p = CoordToScreen(part->hull[i].x, part->hull[i].y);
3644 draw->PathLineTo(p);
3645 }
3646 draw->PathStroke(m_colors.partHullColor, true, 1.0f);
3647 }
3648
3649 /*
3650 * Draw any icon/mark featuers to illustrate the part better
3651 */
3652 if (part->component_type == part->kComponentTypeCapacitor) {
3653 if (part->expanse > 90) {
3654 int segments = trunc(part->expanse);
3655 if (segments < 8) segments = 8;
3656 if (segments > 36) segments = 36;
3657 draw->AddCircle(CoordToScreen(part->centerpoint.x, part->centerpoint.y),
3658 (part->expanse / 3) * m_scale,
3659 m_colors.partOutlineColor & 0x8fffffff,
3660 segments);
3661 }
3662 }
3663
3664 /*
3665 * Draw the text associated with the box or pins if required
3666 */
3667 if (PartIsHighlighted(part) && !part->is_dummy() && !part->name.empty()) {
3668 std::string text = part->name;
3669 std::string mcode = part->mfgcode;
3670
3671 ImVec2 text_size = ImGui::CalcTextSize(text.c_str());
3672 ImVec2 mfgcode_size = ImGui::CalcTextSize(mcode.c_str());
3673
3674 if ((!showInfoPanel) && (mfgcode_size.x > text_size.x)) text_size.x = mfgcode_size.x;
3675
3676 float top_y = a.y;
3677
3678 if (c.y < top_y) top_y = c.y;
3679 ImVec2 pos = ImVec2((a.x + c.x) * 0.5f, top_y);
3680
3681 pos.y -= text_size.y * 2;
3682 if (mcode.size()) pos.y -= text_size.y;
3683
3684 pos.x -= text_size.x * 0.5f;
3685 draw->ChannelsSetCurrent(kChannelText);
3686
3687 // This is the background of the part text.
3688 draw->AddRectFilled(ImVec2(pos.x - DPIF(2.0f), pos.y - DPIF(2.0f)),
3689 ImVec2(pos.x + text_size.x + DPIF(2.0f), pos.y + text_size.y + DPIF(2.0f)),
3690 m_colors.partTextBackgroundColor,
3691 0.0f);
3692 draw->AddText(pos, m_colors.partTextColor, text.c_str());
3693 if ((!showInfoPanel) && (mcode.size())) {
3694 // pos.y += text_size.y;
3695 pos.y += text_size.y + DPIF(2.0f);
3696 draw->AddRectFilled(ImVec2(pos.x - DPIF(2.0f), pos.y - DPIF(2.0f)),
3697 ImVec2(pos.x + text_size.x + DPIF(2.0f), pos.y + text_size.y + DPIF(2.0f)),
3698 m_colors.annotationPopupBackgroundColor,
3699 0.0f);
3700 draw->AddText(ImVec2(pos.x, pos.y), m_colors.annotationPopupTextColor, mcode.c_str());
3701 }
3702 draw->ChannelsSetCurrent(kChannelPolylines);
3703 }
3704 }
3705 } // for each part
3706}
3707
3708void BoardView::DrawPartTooltips(ImDrawList *draw) {
3709 ImVec2 spos = ImGui::GetMousePos();
3710 ImVec2 pos = ScreenToCoord(spos.x, spos.y);
3711
3712 // if (m_parent_occluded) return;
3713 if (!m_tooltips_enabled) return;
3714 if (spos.x > m_board_surface.x) return;
3715 /*
3716 * I am loathing that I have to add this, but basically check every pin on the board so we can
3717 * determine if we're hovering over a testpad
3718 */
3719 for (auto &pin : m_board->Pins()) {
3720
3721 if (pin->type == Pin::kPinTypeTestPad) {
3722 float dx = pin->position.x - pos.x;
3723 float dy = pin->position.y - pos.y;
3724 float dist = dx * dx + dy * dy;
3725 if ((dist < (pin->diameter * pin->diameter))) {
3726 float pd = pin->diameter * m_scale;
3727
3728 draw->AddCircle(CoordToScreen(pin->position.x, pin->position.y), pd, m_colors.pinHaloColor, 32, pinHaloThickness);
3729 ImGui::PushStyleColor(ImGuiCol_Text, ImColor(m_colors.annotationPopupTextColor));
3730 ImGui::PushStyleColor(ImGuiCol_PopupBg, ImColor(m_colors.annotationPopupBackgroundColor));
3731 ImGui::BeginTooltip();
3732 ImGui::Text("TP[%s]%s", pin->number.c_str(), pin->net->name.c_str());
3733 ImGui::EndTooltip();
3734 ImGui::PopStyleColor(2);
3735 break;
3736 } // if in the required diameter
3737 }
3738 }
3739
3740 currentlyHoveredPart = nullptr;
3741 for (auto &part : m_board->Components()) {
3742 int hit = 0;
3743 // auto p_part = part.get();
3744
3745 if (!ComponentIsVisible(part)) continue;
3746
3747 // Work out if the point is inside the hull
3748 {
3749 int i, j, n;
3750 outline_pt *poly;
3751
3752 n = 4;
3753 poly = part->outline;
3754
3755 for (i = 0, j = n - 1; i < n; j = i++) {
3756 if (((poly[i].y > pos.y) != (poly[j].y > pos.y)) &&
3757 (pos.x < (poly[j].x - poly[i].x) * (pos.y - poly[i].y) / (poly[j].y - poly[i].y) + poly[i].x))
3758 hit ^= 1;
3759 }
3760 }
3761
3762 // If we're inside a part
3763 if (hit) {
3764 currentlyHoveredPart = part;
3765 // fprintf(stderr,"InPart: %s\n", currentlyHoveredPart->name.c_str());
3766 if (part->outline_done) {
3767
3768 /*
3769 * Draw the bounding box for the part
3770 */
3771 ImVec2 a, b, c, d;
3772
3773 a = ImVec2(CoordToScreen(part->outline[0].x, part->outline[0].y));
3774 b = ImVec2(CoordToScreen(part->outline[1].x, part->outline[1].y));
3775 c = ImVec2(CoordToScreen(part->outline[2].x, part->outline[2].y));
3776 d = ImVec2(CoordToScreen(part->outline[3].x, part->outline[3].y));
3777 draw->AddQuad(a, b, c, d, m_colors.partHighlightedColor, 2);
3778 }
3779
3780 float min_dist = m_pinDiameter / 2.0f;
3781 min_dist *= min_dist; // all distance squared
3782 currentlyHoveredPin = nullptr;
3783
3784 for (auto &pin : currentlyHoveredPart->pins) {
3785 // auto p = pin;
3786 float dx = pin->position.x - pos.x;
3787 float dy = pin->position.y - pos.y;
3788 float dist = dx * dx + dy * dy;
3789 if ((dist < (pin->diameter * pin->diameter)) && (dist < min_dist)) {
3790 currentlyHoveredPin = pin;
3791 // fprintf(stderr,"Pinhit: %s\n",pin->number.c_str());
3792 min_dist = dist;
3793 } // if in the required diameter
3794 } // for each pin in the part
3795
3796 draw->ChannelsSetCurrent(kChannelAnnotations);
3797
3798 if (currentlyHoveredPin)
3799 draw->AddCircle(CoordToScreen(currentlyHoveredPin->position.x, currentlyHoveredPin->position.y),
3800 currentlyHoveredPin->diameter * m_scale,
3801 m_colors.pinHaloColor,
3802 32,
3803 pinHaloThickness);
3804 ImGui::PushStyleColor(ImGuiCol_Text, ImColor(m_colors.annotationPopupTextColor));
3805 ImGui::PushStyleColor(ImGuiCol_PopupBg, ImColor(m_colors.annotationPopupBackgroundColor));
3806 ImGui::BeginTooltip();
3807 if (currentlyHoveredPin) {
3808 ImGui::Text("%s\n[%s]%s",
3809 currentlyHoveredPart->name.c_str(),
3810 (currentlyHoveredPin ? currentlyHoveredPin->number.c_str() : " "),
3811 (currentlyHoveredPin ? currentlyHoveredPin->net->name.c_str() : " "));
3812 } else {
3813 ImGui::Text("%s", currentlyHoveredPart->name.c_str());
3814 }
3815 ImGui::EndTooltip();
3816 ImGui::PopStyleColor(2);
3817 }
3818
3819 } // for each part on the board
3820}
3821
3822inline void BoardView::DrawPinTooltips(ImDrawList *draw) {
3823 if (!m_tooltips_enabled) return;
3824 draw->ChannelsSetCurrent(kChannelAnnotations);
3825
3826 if (HighlightedPinIsHovered()) {
3827 ImGui::PushStyleColor(ImGuiCol_Text, ImColor(m_colors.annotationPopupTextColor));
3828 ImGui::PushStyleColor(ImGuiCol_PopupBg, ImColor(m_colors.annotationPopupBackgroundColor));
3829 ImGui::BeginTooltip();
3830 ImGui::Text("%s[%s]\n%s",
3831 m_pinHighlightedHovered->component->name.c_str(),
3832 m_pinHighlightedHovered->number.c_str(),
3833 m_pinHighlightedHovered->net->name.c_str());
3834 ImGui::EndTooltip();
3835 ImGui::PopStyleColor(2);
3836 }
3837}
3838
3839inline void BoardView::DrawAnnotations(ImDrawList *draw) {
3840
3841 if (!showAnnotations) return;
3842 if (!m_tooltips_enabled) return;
3843
3844 draw->ChannelsSetCurrent(kChannelAnnotations);
3845
3846 for (auto &ann : m_annotations.annotations) {
3847 if (ann.side == m_current_side) {
3848 ImVec2 a, b, s;
3849 if (debug) fprintf(stderr, "%d:%d:%f %f: %s\n", ann.id, ann.side, ann.x, ann.y, ann.note.c_str());
3850 a = s = CoordToScreen(ann.x, ann.y);
3851 a.x += annotationBoxOffset;
3852 a.y -= annotationBoxOffset;
3853 b = ImVec2(a.x + annotationBoxSize, a.y - annotationBoxSize);
3854
3855 if ((ann.hovered == true) && (m_tooltips_enabled)) {
3856 char buf[60];
3857
3858 snprintf(buf, sizeof(buf), "%s", ann.note.c_str());
3859 buf[50] = '\0';
3860
3861 ImGui::PushStyleColor(ImGuiCol_Text, ImColor(m_colors.annotationPopupTextColor));
3862 ImGui::PushStyleColor(ImGuiCol_PopupBg, ImColor(m_colors.annotationPopupBackgroundColor));
3863 ImGui::BeginTooltip();
3864 ImGui::Text("%c(%0.0f,%0.0f) %s %s%c%s%c\n%s%s",
3865 m_current_side ? 'B' : 'T',
3866 ann.x,
3867 ann.y,
3868 ann.net.c_str(),
3869 ann.part.c_str(),
3870 ann.part.size() && ann.pin.size() ? '[' : ' ',
3871 ann.pin.c_str(),
3872 ann.part.size() && ann.pin.size() ? ']' : ' ',
3873 buf,
3874 ann.note.size() > 50 ? "..." : "");
3875
3876 ImGui::EndTooltip();
3877 ImGui::PopStyleColor(2);
3878 } else {
3879 }
3880 draw->AddCircleFilled(s, DPIF(2), m_colors.annotationStalkColor, 8);
3881 draw->AddRectFilled(a, b, m_colors.annotationBoxColor);
3882 draw->AddRect(a, b, m_colors.annotationStalkColor);
3883 draw->AddLine(s, a, m_colors.annotationStalkColor);
3884 }
3885 }
3886}
3887
3888bool BoardView::HighlightedPinIsHovered(void) {
3889 ImVec2 mp = ImGui::GetMousePos();
3890 ImVec2 mpc = ScreenToCoord(mp.x, mp.y); // it's faster to compute this once than convert all pins
3891
3892 m_pinHighlightedHovered = nullptr;
3893
3894 /*
3895 * See if any of the pins listed in the m_pinHighlighted vector are hovered over
3896 */
3897 for (auto &p : m_pinHighlighted) {
3898 ImVec2 a = ImVec2(p->position.x, p->position.y);
3899 double r = p->diameter / 2.0f;
3900 if ((mpc.x > a.x - r) && (mpc.x < a.x + r) && (mpc.y > a.y - r) && (mpc.y < a.y + r)) {
3901 m_pinHighlightedHovered = p;
3902 return true;
3903 }
3904 }
3905
3906 /*
3907 * See if any of the pins in the same network as the SELECTED pin (single) are hovered
3908 */
3909 for (auto &p : m_board->Pins()) {
3910 // auto p = pin.get();
3911 double r = p->diameter / 2.0f * m_scale;
3912 if (m_pinSelected && p->net == m_pinSelected->net) {
3913 ImVec2 a = ImVec2(p->position.x, p->position.y);
3914 if ((mpc.x > a.x - r) && (mpc.x < a.x + r) && (mpc.y > a.y - r) && (mpc.y < a.y + r)) {
3915 m_pinHighlightedHovered = p;
3916 return true;
3917 }
3918 }
3919 }
3920
3921 /*
3922 * See if any pins of a highlighted part are hovered
3923 */
3924 for (auto &part : m_partHighlighted) {
3925 for (auto &p : part->pins) {
3926 double r = p->diameter / 2.0f * m_scale;
3927 ImVec2 a = ImVec2(p->position.x, p->position.y);
3928 if ((mpc.x > a.x - r) && (mpc.x < a.x + r) && (mpc.y > a.y - r) && (mpc.y < a.y + r)) {
3929 m_pinHighlightedHovered = p;
3930 return true;
3931 }
3932 }
3933 }
3934
3935 return false;
3936}
3937
3938int BoardView::AnnotationIsHovered(void) {
3939 ImVec2 mp = ImGui::GetMousePos();
3940 bool is_hovered = false;
3941 int i = 0;
3942
3943 if (!m_tooltips_enabled) return false;
3944 m_annotation_last_hovered = 0;
3945
3946 for (auto &ann : m_annotations.annotations) {
3947 ImVec2 a = CoordToScreen(ann.x, ann.y);
3948 if ((mp.x > a.x + annotationBoxOffset) && (mp.x < a.x + (annotationBoxOffset + annotationBoxSize)) &&
3949 (mp.y < a.y - annotationBoxOffset) && (mp.y > a.y - (annotationBoxOffset + annotationBoxSize))) {
3950 ann.hovered = true;
3951 is_hovered = true;
3952 m_annotation_last_hovered = i;
3953 } else {
3954 ann.hovered = false;
3955 }
3956 i++;
3957 }
3958
3959 if (is_hovered == false) m_annotation_clicked_id = -1;
3960
3961 return is_hovered;
3962}
3963
3964/*
3965 * TODO
3966 * EXPERIMENTAL, draw an area around selected pins
3967 */
3968void BoardView::DrawSelectedPins(ImDrawList *draw) {
3969 ImVec2 pl[1024];
3970 ImVec2 hull[1024];
3971 int i = 0, hpc = 0;
3972 if ((m_pinHighlighted.size()) < 3) return;
3973 for (auto &p : m_pinHighlighted) {
3974 pl[i] = CoordToScreen(p->position.x, p->position.y);
3975 i++;
3976 }
3977 hpc = VHConvexHull(hull, pl, i);
3978 if (hpc > 3) {
3979 draw->AddConvexPolyFilled(hull, hpc, ImColor(0x660000ff), false);
3980 }
3981}
3982
3983void BoardView::DrawBoard() {
3984 if (!m_file || !m_board) return;
3985
3986 ImDrawList *draw = ImGui::GetWindowDrawList();
3987 if (!m_needsRedraw) {
3988 memcpy(draw, m_cachedDrawList, sizeof(ImDrawList));
3989 memcpy(draw->CmdBuffer.Data, m_cachedDrawCommands.Data, m_cachedDrawCommands.Size);
3990 return;
3991 }
3992
3993 // Splitting channels, drawing onto those and merging back.
3994 draw->ChannelsSplit(NUM_DRAW_CHANNELS);
3995
3996 // We draw the Parts before the Pins so that we can ascertain the needed pin
3997 // size for the parts based on the part/pad geometry and spacing. -Inflex
3998 // OutlineGenerateFill();
3999 // DrawFill(draw);
4000 OutlineGenFillDraw(draw, boardFillSpacing, 1);
4001 DrawOutline(draw);
4002 DrawParts(draw);
4003 // DrawSelectedPins(draw);
4004 DrawPins(draw);
4005 // DrawPinTooltips(draw);
4006 DrawPartTooltips(draw);
4007 DrawAnnotations(draw);
4008
4009 draw->ChannelsMerge();
4010
4011 // Copy the new draw list and cmd buffer:
4012 memcpy(m_cachedDrawList, draw, sizeof(ImDrawList));
4013 int cmds_size = draw->CmdBuffer.size() * sizeof(ImDrawCmd);
4014 m_cachedDrawCommands.resize(cmds_size);
4015 memcpy(m_cachedDrawCommands.Data, draw->CmdBuffer.Data, cmds_size);
4016 m_needsRedraw = false;
4017}
4018/** end of drawing region **/
4019
4020int qsort_netstrings(const void *a, const void *b) {
4021 const char *sa = *(const char **)a;
4022 const char *sb = *(const char **)b;
4023 return strcmp(sa, sb);
4024}
4025
4026/**
4027 * CenterView
4028 *
4029 * Resets the scale and transformation back to original.
4030 * Does NOT change the rotation (yet?)
4031 *
4032 * PLD20160621-1715
4033 *
4034 */
4035void BoardView::CenterView(void) {
4036 // ImVec2 view = ImGui::GetIO().DisplaySize;
4037 ImVec2 view = m_board_surface;
4038
4039 float dx = 1.1f * (m_boardWidth);
4040 float dy = 1.1f * (m_boardHeight);
4041 float sx = dx > 0 ? view.x / dx : 1.0f;
4042 float sy = dy > 0 ? view.y / dy : 1.0f;
4043
4044 // m_rotation = 0;
4045 m_scale_floor = m_scale = sx < sy ? sx : sy;
4046 SetTarget(m_mx, m_my);
4047 m_needsRedraw = true;
4048}
4049
4050void BoardView::SetFile(BRDFile *file) {
4051 delete m_file;
4052 delete m_board;
4053
4054 m_file = file;
4055 m_board = new BRDBoard(file);
4056 searcher.setParts(m_board->Components());
4057 searcher.setNets(m_board->Nets());
4058
4059 std::vector<std::string> netnames;
4060 for (auto &n : m_board->Nets()) netnames.push_back(n->name);
4061 std::vector<std::string> partnames;
4062 for (auto &p : m_board->Components()) netnames.push_back(p->name);
4063
4064 scnets.setDictionary(netnames);
4065 scparts.setDictionary(partnames);
4066
4067 m_nets = m_board->Nets();
4068
4069 int min_x = INT_MAX, max_x = INT_MIN, min_y = INT_MAX, max_y = INT_MIN;
4070 for (auto &pa : m_file->format) {
4071 if (pa.x < min_x) min_x = pa.x;
4072 if (pa.y < min_y) min_y = pa.y;
4073 if (pa.x > max_x) max_x = pa.x;
4074 if (pa.y > max_y) max_y = pa.y;
4075 }
4076
4077 // ImVec2 view = ImGui::GetIO().DisplaySize;
4078 ImVec2 view = m_board_surface;
4079
4080 m_mx = (float)(min_x + max_x) / 2.0f;
4081 m_my = (float)(min_y + max_y) / 2.0f;
4082
4083 float dx = 1.1f * (max_x - min_x);
4084 float dy = 1.1f * (max_y - min_y);
4085 float sx = dx > 0 ? view.x / dx : 1.0f;
4086 float sy = dy > 0 ? view.y / dy : 1.0f;
4087
4088 m_scale_floor = m_scale = sx < sy ? sx : sy;
4089 m_boardWidth = max_x - min_x;
4090 m_boardHeight = max_y - min_y;
4091 SetTarget(m_mx, m_my);
4092
4093 m_pinHighlighted.reserve(m_board->Components().size());
4094 m_partHighlighted.reserve(m_board->Components().size());
4095 m_pinSelected = nullptr;
4096
4097 m_firstFrame = true;
4098 m_needsRedraw = true;
4099}
4100
4101ImVec2 BoardView::CoordToScreen(float x, float y, float w) {
4102 float side = m_current_side ? -1.0f : 1.0f;
4103 float tx = side * m_scale * (x + w * (m_dx - m_mx));
4104 float ty = -1.0f * m_scale * (y + w * (m_dy - m_my));
4105 switch (m_rotation) {
4106 case 0: return ImVec2(tx, ty);
4107 case 1: return ImVec2(-ty, tx);
4108 case 2: return ImVec2(-tx, -ty);
4109 default: return ImVec2(ty, -tx);
4110 }
4111}
4112
4113ImVec2 BoardView::ScreenToCoord(float x, float y, float w) {
4114 float tx, ty;
4115
4116 switch (m_rotation) {
4117 case 0:
4118 tx = x;
4119 ty = y;
4120 break;
4121 case 1:
4122 tx = y;
4123 ty = -x;
4124 break;
4125 case 2:
4126 tx = -x;
4127 ty = -y;
4128 break;
4129 default:
4130 tx = -y;
4131 ty = x;
4132 break;
4133 }
4134 float side = m_current_side ? -1.0f : 1.0f;
4135 float invscale = 1.0f / m_scale;
4136
4137 tx = tx * side * invscale + w * (m_mx - m_dx);
4138 ty = ty * -1.0f * invscale + w * (m_my - m_dy);
4139
4140 return ImVec2(tx, ty);
4141}
4142
4143void BoardView::Rotate(int count) {
4144 // too lazy to do math
4145 while (count > 0) {
4146 m_rotation = (m_rotation + 1) & 3;
4147 float dx = m_dx;
4148 float dy = m_dy;
4149 if (m_current_side == 0) {
4150 m_dx = -dy;
4151 m_dy = dx;
4152 } else {
4153 m_dx = dy;
4154 m_dy = -dx;
4155 }
4156 --count;
4157 m_needsRedraw = true;
4158 }
4159 while (count < 0) {
4160 m_rotation = (m_rotation - 1) & 3;
4161 float dx = m_dx;
4162 float dy = m_dy;
4163 if (m_current_side == 1) {
4164 m_dx = -dy;
4165 m_dy = dx;
4166 } else {
4167 m_dx = dy;
4168 m_dy = -dx;
4169 }
4170 ++count;
4171 m_needsRedraw = true;
4172 }
4173}
4174
4175void BoardView::Mirror(void) {
4176 auto &outline = m_board->OutlinePoints();
4177 ImVec2 min, max;
4178
4179 // find the orthagonal bounding box
4180 // probably can put this as a predefined
4181 min.x = min.y = FLT_MAX;
4182 max.x = max.y = FLT_MIN;
4183 for (auto &p : outline) {
4184 if (p->x < min.x) min.x = p->x;
4185 if (p->y < min.y) min.y = p->y;
4186 if (p->x > max.x) max.x = p->x;
4187 if (p->y > max.y) max.y = p->y;
4188 }
4189
4190 for (auto &p : outline) {
4191 p->x = max.x - p->x;
4192 }
4193
4194 for (auto &p : m_board->Pins()) {
4195 // auto p = pin.get();
4196 p->position.x = max.x - p->position.x;
4197 }
4198
4199 for (auto &part : m_board->Components()) {
4200
4201 part->centerpoint.x = max.x - part->centerpoint.x;
4202
4203 if (part->outline_done) {
4204 for (int i = 0; i < 4; i++) {
4205 part->outline[i].x = max.x - part->outline[i].x;
4206 }
4207 }
4208
4209 for (int i = 0; i < part->hull_count; i++) {
4210 part->hull[i].x = max.x - part->hull[i].x;
4211 }
4212 }
4213
4214 for (auto &ann : m_annotations.annotations) {
4215 ann.x = max.x - ann.x;
4216 }
4217}
4218
4219void BoardView::SetTarget(float x, float y) {
4220 // ImVec2 view = ImGui::GetIO().DisplaySize;
4221 ImVec2 view = m_board_surface;
4222 ImVec2 coord = ScreenToCoord(view.x / 2.0f, view.y / 2.0f);
4223 m_dx += coord.x - x;
4224 m_dy += coord.y - y;
4225}
4226
4227inline bool BoardView::ComponentIsVisible(const std::shared_ptr<Component> part) {
4228 if (!part) return true; // no component? => no board side info
4229
4230 if (part->board_side == m_current_side) return true;
4231
4232 if (part->board_side == kBoardSideBoth) return true;
4233
4234 return false;
4235}
4236
4237inline bool BoardView::IsVisibleScreen(float x, float y, float radius, const ImGuiIO &io) {
4238 // if (x < -radius || y < -radius || x - radius > io.DisplaySize.x || y - radius > io.DisplaySize.y) return false;
4239 if (x < -radius || y < -radius || x - radius > m_board_surface.x || y - radius > m_board_surface.y) return false;
4240 return true;
4241}
4242
4243bool BoardView::PartIsHighlighted(const std::shared_ptr<Component> component) {
4244 bool highlighted = contains(component, m_partHighlighted);
4245
4246 // is any pin of this part selected?
4247 if (m_pinSelected) highlighted |= m_pinSelected->component == component;
4248
4249 return highlighted;
4250}
4251
4252bool BoardView::AnyItemVisible(void) {
4253 bool any_visible = false;
4254
4255 if (m_searchComponents) {
4256 for (auto &p : m_partHighlighted) {
4257 any_visible |= ComponentIsVisible(p);
4258 }
4259 }
4260
4261 if (!any_visible) {
4262 if (m_searchNets) {
4263 for (auto &p : m_pinHighlighted) {
4264 any_visible |= ComponentIsVisible(p->component);
4265 }
4266 }
4267 }
4268
4269 return any_visible;
4270}
4271
4272void BoardView::FindNetNoClear(const char *name) {
4273 if (!m_file || !m_board || !(*name)) return;
4274
4275 auto results = searcher.nets(name);
4276
4277 for (auto &net : results) {
4278 for (auto &pin : net->pins)
4279 m_pinHighlighted.push_back(pin);
4280 }
4281}
4282
4283void BoardView::FindNet(const char *name) {
4284 m_pinHighlighted.clear();
4285 FindNetNoClear(name);
4286}
4287
4288void BoardView::FindComponentNoClear(const char *name) {
4289 if (!m_file || !m_board || !name) return;
4290
4291 auto results = searcher.parts(name);
4292
4293 for (auto &p : results) {
4294 m_partHighlighted.push_back(p);
4295
4296 for (auto &pin : p->pins) {
4297 m_pinHighlighted.push_back(pin);
4298 }
4299 }
4300 m_needsRedraw = true;
4301}
4302
4303void BoardView::FindComponent(const char *name) {
4304 if (!m_file || !m_board) return;
4305
4306 m_pinHighlighted.clear();
4307 m_partHighlighted.clear();
4308
4309 FindComponentNoClear(name);
4310}
4311
4312void BoardView::SearchCompoundNoClear(const char *item) {
4313 if (*item == '\0') return;
4314 if (debug) fprintf(stderr, "Searching for '%s'\n", item);
4315 if (m_searchComponents) FindComponentNoClear(item);
4316 if (m_searchNets) FindNetNoClear(item);
4317 if (!m_partHighlighted.empty() && !m_pinHighlighted.empty() && !AnyItemVisible()) FlipBoard(1); // passing 1 to override flipBoard parameter
4318}
4319
4320void BoardView::SearchCompound(const char *item) {
4321 if (*item == '\0') return;
4322 m_pinHighlighted.clear();
4323 m_partHighlighted.clear();
4324 // ClearAllHighlights();
4325
4326 SearchCompoundNoClear(item);
4327}
4328
4329void BoardView::SetLastFileOpenName(const std::string &name) {
4330 m_lastFileOpenName = name;
4331}
4332
4333void BoardView::FlipBoard(int mode) {
4334 ImVec2 mpos = ImGui::GetMousePos();
4335 // ImVec2 view = ImGui::GetIO().DisplaySize;
4336 ImVec2 view = m_board_surface;
4337 ImVec2 bpos = ScreenToCoord(mpos.x, mpos.y);
4338 auto io = ImGui::GetIO();
4339
4340 if (mode == 1)
4341 mode = 0;
4342 else
4343 mode = flipMode;
4344
4345 m_current_side ^= 1;
4346 m_dx = -m_dx;
4347 if (m_flipVertically) {
4348 Rotate(2);
4349 if (io.KeyShift ^ mode) {
4350 SetTarget(bpos.x, bpos.y);
4351 Pan(DIR_RIGHT, view.x / 2 - mpos.x);
4352 Pan(DIR_DOWN, view.y / 2 - mpos.y);
4353 }
4354 }
4355 m_needsRedraw = true;
4356}
4357
4358BitVec::~BitVec() {
4359 free(m_bits);
4360}
4361
4362void BitVec::Resize(uint32_t new_size) {
4363 if (new_size > m_size) {
4364 uint32_t bytelen = 4 * ((m_size + 31) / 32);
4365 uint32_t new_bytelen = 4 * ((new_size + 31) / 32);
4366 uint32_t *new_bits = (uint32_t *)malloc(new_bytelen);
4367 if (m_bits) {
4368 memcpy(new_bits, m_bits, bytelen);
4369 free(m_bits);
4370 memset((char *)new_bits + bytelen, 0, new_bytelen - bytelen);
4371 } else {
4372 memset(new_bits, 0, new_bytelen);
4373 }
4374 m_bits = new_bits;
4375 }
4376 m_size = new_size;
4377}