1 /*****************************************************************************/
2 /* LibreDWG - free implementation of the DWG file format */
3 /* */
4 /* Copyright (C) 2019,2022-2023 Free Software Foundation, Inc. */
5 /* */
6 /* This library is free software, licensed under the terms of the GNU */
7 /* General Public License as published by the Free Software Foundation, */
8 /* either version 3 of the License, or (at your option) any later version. */
9 /* You should have received a copy of the GNU General Public License */
10 /* along with this program. If not, see <http://www.gnu.org/licenses/>. */
11 /*****************************************************************************/
12
13 /*
14 * geom.c: geometric projections from OCS
15 * Note: There are certainly bugs lurking here. Not thoroughly tested yet.
16 * For the properly exported variants see dwg_api instead, with the
17 * dwg_geom_ prefix.
18 * written by Reini Urban
19 */
20
21 #include "config.h"
22 #include <string.h>
23 // #include <stdio.h>
24 #include <math.h>
25 #include <dwg.h>
26 #include "common.h"
27 #include "geom.h"
28
29 static void
30 normalize (BITCODE_3DPOINT *out, BITCODE_3DPOINT pt)
31 {
32 double l = sqrt ((pt.x * pt.x) + (pt.y * pt.y) + (pt.z * pt.z));
33 *out = pt;
34 if (l != 1.0 && l != 0.0)
35 {
36 out->x = pt.x / l;
37 out->y = pt.y / l;
38 out->z = pt.z / l;
39 }
40 }
41
42 static void
43 cross (BITCODE_3DPOINT *out, BITCODE_3DPOINT pt1, BITCODE_3DPOINT pt2)
44 {
45 out->x = pt1.y * pt2.z - pt1.z * pt2.y;
46 out->y = pt1.z * pt2.x - pt1.x * pt2.z;
47 out->z = pt1.x * pt2.y - pt1.y * pt2.x;
48 }
49
50 // transform a 2D point via its OCS (extrusion or normal) to 2D
51 void
52 transform_OCS_2d (BITCODE_2DPOINT *out, BITCODE_2DPOINT pt, BITCODE_BE ext)
53 {
54 // [0,0,0] with preR13
55 if (ext.x == 0.0 && ext.y == 0.0 && (ext.z == 1.0 || ext.z == 0.0))
56 {
57 *out = pt;
58 }
59 else if (ext.x == 0.0 && ext.y == 0.0 && ext.z == -1.0)
60 {
61 *out = pt;
62 out->x = -out->x;
63 }
64 else
65 {
66 /* This is called the "Arbitrary Axis Algorithm" to calculate
67 the OCS x-axis from the extrusion z-vector (the "normal") */
68 BITCODE_3DPOINT ax, ay, az, be;
69 memcpy (&be, &ext, sizeof (BITCODE_3DPOINT));
70 normalize (&az, be);
71 if ((fabs (az.x) < 1 / 64.0) && (fabs (az.y) < 1 / 64.0))
72 {
73 BITCODE_3DPOINT tmp = { 0.0, 1.0, 0.0 };
74 cross (&tmp, tmp, az);
75 normalize (&ax, tmp);
76 }
77 else
78 {
79 BITCODE_3DPOINT tmp = { 0.0, 0.0, 1.0 };
80 cross (&tmp, tmp, az);
81 normalize (&ax, tmp);
82 }
83 cross (&ay, az, ax);
84 normalize (&ay, ay);
85 out->x = pt.x * ax.x + pt.y * ax.y;
86 out->y = pt.x * ay.x + pt.y * ay.y;
87 }
88 return;
89 }
90
91 // transform a 3D point via its OCS (extrusion or normal) to 3D
92 void
93 transform_OCS (BITCODE_3DPOINT *out, BITCODE_3DPOINT pt, BITCODE_BE ext)
94 {
95 if (ext.x == 0.0 && ext.y == 0.0 && ext.z == 1.0)
96 {
97 *out = pt;
98 }
99 else if (ext.x == 0.0 && ext.y == 0.0 && ext.z == -1.0)
100 {
101 *out = pt;
102 out->x = -out->x;
103 }
104 else
105 {
106 /* This is called the "Arbitrary Axis Algorithm" to calculate
107 the OCS x-axis from the extrusion z-vector */
108 BITCODE_3DPOINT ax, ay, az, be;
109 memcpy (&be, &ext, sizeof (BITCODE_3DPOINT));
110 normalize (&az, be);
111 if ((fabs (az.x) < 1 / 64.0) && (fabs (az.y) < 1 / 64.0))
112 {
113 BITCODE_3DPOINT tmp = { 0.0, 1.0, 0.0 };
114 cross (&tmp, tmp, az);
115 normalize (&ax, tmp);
116 }
117 else
118 {
119 BITCODE_3DPOINT tmp = { 0.0, 0.0, 1.0 };
120 cross (&tmp, tmp, az);
121 normalize (&ax, tmp);
122 }
123 cross (&ay, az, ax);
124 normalize (&ay, ay);
125 out->x = pt.x * ax.x + pt.y * ax.y + pt.z * ax.z;
126 out->y = pt.x * ay.x + pt.y * ay.y + pt.z * ay.z;
127 out->z = pt.x * az.x + pt.y * az.y + pt.z * az.z;
128 }
129 return;
130 }
131
132 // TODO: bulge -> arc for svg and ps.
133
134 // endpoint of the angular vector from ctr, to angle (radian), for radius len
135 void
136 angle_vector_2d (BITCODE_2BD *out, BITCODE_2BD ctr, BITCODE_BD angle,
137 BITCODE_BD len)
138 {
139 out->x = ctr.x + (len * cos (angle));
140 out->y = ctr.y + (len * sin (angle));
141 }
142
143 // Segmentation of arc,curves into plines for geojson.
144 void
145 arc_split (BITCODE_2BD *pts, const int num_pts, const BITCODE_2BD ctr,
146 BITCODE_BD start_angle, BITCODE_BD end_angle, const BITCODE_BD len)
147 {
148 double ang, angd;
149 #ifndef HAVE_NONNULL
150 if (!pts)
151 return;
152 #endif
153 while (start_angle > end_angle)
154 end_angle += M_PI;
155 // shoot vectors from ctr to ang
156 ang = start_angle;
157 angd = (end_angle - start_angle) / num_pts;
158 while ((angd = (end_angle - start_angle) / (num_pts - 1)) < 0)
159 start_angle += M_PI;
160 // fprintf (stderr, "ctr (%g,%g) ang: %g - %g\n", ctr.x, ctr.y, ang,
161 // end_angle);
162 for (int i = 0; i < num_pts; i++, ang += angd)
163 {
164 BITCODE_2BD pt;
165 angle_vector_2d (&pt, ctr, ang, len);
166 // fprintf (stderr, "ang[%d] %g\n", i, ang);
167 pts[i].x = pt.x;
168 pts[i].y = pt.y;
169 }
170 }