Engauge Digitizer  2
 All Classes Functions Variables Typedefs Enumerations Friends Pages
mmsubs.cpp
1 /******************************************************************************************************
2  * (C) 2014 markummitchell@github.com. This file is part of Engauge Digitizer, which is released *
3  * under GNU General Public License version 2 (GPLv2) or (at your option) any later version. See file *
4  * LICENSE or go to gnu.org/licenses for details. Distribution requires prior written permission. *
5  ******************************************************************************************************/
6 
7 #include "mmsubs.h"
8 #include <QImage>
9 #include <QPointF>
10 #include <qmath.h>
11 
12 const double PI = 3.1415926535;
13 
14 double angleBetweenVectors (const QPointF &v1,
15  const QPointF &v2)
16 {
17  double v1Mag = qSqrt (v1.x() * v1.x() + v1.y() * v1.y());
18  double v2Mag = qSqrt (v2.x() * v2.x() + v2.y() * v2.y());
19 
20  double angle = 0;
21  if ((v1Mag > 0) || (v2Mag > 0)) {
22 
23  double cosArg = (v1.x() * v2.x() + v1.y() * v2.y()) / (v1Mag * v2Mag);
24  cosArg = qMin (qMax (cosArg, -1.0), 1.0);
25  angle = qAcos (cosArg);
26  }
27 
28  return angle;
29 }
30 
31 double angleFromVectorToVector (const QPointF &vFrom,
32  const QPointF &vTo)
33 {
34  double angleFrom = qAtan2 (vFrom.y(), vFrom.x());
35  double angleTo = qAtan2 (vTo.y() , vTo.x());
36 
37  // Rotate both angles to put from vector along x axis. Note that angleFrom-angleFrom is zero,
38  // and angleTo-angleFrom is -pi to +pi radians
39  double angleSeparation = angleTo - angleFrom;
40 
41  while (angleSeparation < -1.0 * PI) {
42  angleSeparation += 2.0 * PI;
43  }
44  while (angleSeparation > PI) {
45  angleSeparation -= 2.0 * PI;
46  }
47 
48  return angleSeparation;
49 }
50 
51 QRgb pixelRGB(const QImage &image, int x, int y)
52 {
53  switch (image.depth())
54  {
55  case 1:
56  return pixelRGB1(image, x, y);
57  case 8:
58  return pixelRGB8(image, x, y);
59  default:
60  return pixelRGB32(image, x, y);
61  }
62 }
63 
64 QRgb pixelRGB1(const QImage &image1Bit, int x, int y)
65 {
66  unsigned int bit;
67  if (image1Bit.format () == QImage::Format_MonoLSB) {
68  bit = *(image1Bit.scanLine (y) + (x >> 3)) & (1 << (x & 7));
69  } else {
70  bit = *(image1Bit.scanLine (y) + (x >> 3)) & (1 << (7 - (x & 7)));
71  }
72 
73  int tableIndex = ((bit == 0) ? 0 : 1);
74  return image1Bit.color(tableIndex);
75 }
76 
77 QRgb pixelRGB8(const QImage &image8Bit, int x, int y)
78 {
79  int tableIndex = *(image8Bit.scanLine(y) + x);
80  return image8Bit.color(tableIndex);
81 }
82 
83 QRgb pixelRGB32(const QImage &image32Bit, int x, int y)
84 {
85  // QImage::scanLine documentation says:
86  // 1) Cast return value to QRgb* (which is 32 bit) which hides platform-specific byte orders
87  // 2) Scanline data is aligned on 32 bit boundary
88  // unsigned int* p = (unsigned int *) image32Bit.scanLine(y) + x;
89  const QRgb *p = reinterpret_cast<const QRgb *> (image32Bit.scanLine(y)) + x;
90  return *p;
91 }
92 
93 void projectPointOntoLine(double xToProject,
94  double yToProject,
95  double xStart,
96  double yStart,
97  double xStop,
98  double yStop,
99  double *xProjection,
100  double *yProjection,
101  double *projectedDistanceOutsideLine,
102  double *distanceToLine)
103 {
104  double s;
105  if (qAbs (yStart - yStop) > qAbs (xStart - xStop)) {
106 
107  // More vertical than horizontal. Compute slope and intercept of y=slope*x+yintercept line through (xToProject, yToProject)
108  double slope = (xStop - xStart) / (yStart - yStop);
109  double yintercept = yToProject - slope * xToProject;
110 
111  // Intersect projected point line (slope-intercept form) with start-stop line (parametric form x=(1-s)*x1+s*x2, y=(1-s)*y1+s*y2)
112  s = (slope * xStart + yintercept - yStart) /
113  (yStop - yStart + slope * (xStart - xStop));
114 
115  } else {
116 
117  // More horizontal than vertical. Compute slope and intercept of x=slope*y+xintercept line through (xToProject, yToProject)
118  double slope = (yStop - yStart) / (xStart - xStop);
119  double xintercept = xToProject - slope * yToProject;
120 
121  // Intersect projected point line (slope-intercept form) with start-stop line (parametric form x=(1-s)*x1+s*x2, y=(1-s)*y1+s*y2)
122  s = (slope * yStart + xintercept - xStart) /
123  (xStop - xStart + slope * (yStart - yStop));
124 
125  }
126 
127  *xProjection = (1.0 - s) * xStart + s * xStop;
128  *yProjection = (1.0 - s) * yStart + s * yStop;
129 
130  if (s < 0) {
131 
132  *projectedDistanceOutsideLine = qSqrt ((*xProjection - xStart) * (*xProjection - xStart) +
133  (*yProjection - yStart) * (*yProjection - yStart));
134  *distanceToLine = qSqrt ((xToProject - xStart) * (xToProject - xStart) +
135  (yToProject - yStart) * (yToProject - yStart));
136 
137  // Bring projection point to inside line
138  *xProjection = xStart;
139  *yProjection = yStart;
140 
141  } else if (s > 1) {
142 
143  *projectedDistanceOutsideLine = qSqrt ((*xProjection - xStop) * (*xProjection - xStop) +
144  (*yProjection - yStop) * (*yProjection - yStop));
145  *distanceToLine = qSqrt ((xToProject - xStop) * (xToProject - xStop) +
146  (yToProject - yStop) * (yToProject - yStop));
147 
148  // Bring projection point to inside line
149  *xProjection = xStop;
150  *yProjection = yStop;
151 
152  } else {
153 
154  *distanceToLine = qSqrt ((xToProject - *xProjection) * (xToProject - *xProjection) +
155  (yToProject - *yProjection) * (yToProject - *yProjection));
156 
157  // Projected point is aleady inside line
158  *projectedDistanceOutsideLine = 0.0;
159 
160  }
161 }
162 
163 void setPixelRGB(QImage &image, int x, int y, QRgb q)
164 {
165  switch (image.depth())
166  {
167  case 1:
168  setPixelRGB1(image, x, y, q);
169  return;
170  case 8:
171  setPixelRGB8(image, x, y, q);
172  return;
173  case 32:
174  setPixelRGB32(image, x, y, q);
175  return;
176  }
177 }
178 
179 void setPixelRGB1(QImage &image1Bit, int x, int y, QRgb q)
180 {
181  for (int index = 0; index < image1Bit.colorCount(); index++) {
182  if (q == image1Bit.color(index))
183  {
184  if (image1Bit.format () == QImage::Format_MonoLSB)
185  {
186  *(image1Bit.scanLine (y) + (x >> 3)) &= ~(1 << (x & 7));
187  if (index > 0)
188  *(image1Bit.scanLine (y) + (x >> 3)) |= index << (x & 7);
189  }
190  else
191  {
192  *(image1Bit.scanLine (y) + (x >> 3)) &= ~(1 << (7 - (x & 7)));
193  if (index > 0)
194  *(image1Bit.scanLine (y) + (x >> 3)) |= index << (7 - (x & 7));
195  }
196  return;
197  }
198  }
199 }
200 
201 void setPixelRGB8(QImage &image8Bit, int x, int y, QRgb q)
202 {
203  for (int index = 0; index < image8Bit.colorCount(); index++) {
204  if (q == image8Bit.color(index))
205  {
206  *(image8Bit.scanLine(y) + x) = static_cast<uchar> (index);
207  return;
208  }
209  }
210 }
211 
212 void setPixelRGB32(QImage &image32Bit, int x, int y, QRgb q)
213 {
214  int* p = (int *)image32Bit.scanLine(y) + x;
215  *p = signed (q);
216 }