SUAPI-CppWrapper
C++WrapperforSketchUpCAPI
Face.cpp
1 //
2 // Face.cpp
3 //
4 // Sketchup C++ Wrapper for C API
5 // MIT License
6 //
7 // Copyright (c) 2017 Tom Kaneko
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 
16 // The above copyright notice and this permission notice shall be included in all
17 // copies or substantial portions of the Software.
18 
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 // SOFTWARE.
26 //
27 
28 // Macro for getting rid of unused variables commonly for assert checking
29 #define _unused(x) ((void)(x))
30 
31 #include <cassert>
32 #include <stdexcept>
33 
34 #include <SketchUpAPI/model/edge.h>
35 
36 #include "SUAPI-CppWrapper/model/Face.hpp"
37 
38 #include "SUAPI-CppWrapper/Geometry.hpp"
39 #include "SUAPI-CppWrapper/model/Material.hpp"
40 #include "SUAPI-CppWrapper/model/Vertex.hpp"
41 #include "SUAPI-CppWrapper/model/Edge.hpp"
42 #include "SUAPI-CppWrapper/model/Loop.hpp"
43 #include "SUAPI-CppWrapper/model/LoopInput.hpp"
44 
45 namespace CW {
46 
47 /**************************
48 * Private static methods **
49 ***************************/
50 SUFaceRef Face::create_face(std::vector<Point3D>& outer_points) {
51  LoopInput loop_input;
52  for (size_t i=0; i < outer_points.size(); ++i) {
53  loop_input.add_vertex_index(i);
54  }
55  return create_face(outer_points, loop_input);
56 }
57 
58 
59 SUFaceRef Face::create_face(std::vector<Point3D>& outer_points, LoopInput& loop_input) {
60  SUFaceRef face = SU_INVALID;
61  SULoopInputRef loop_input_ref = loop_input.ref();
62  std::vector<SUPoint3D> su_points(outer_points.size());
63  std::transform(outer_points.begin(), outer_points.end(), su_points.begin(),
64  [](const Point3D& value){
65  return (SUPoint3D)value;
66  });
67  SUResult res = SUFaceCreate(&face, su_points.data(), &loop_input_ref);
68  if (res != SU_ERROR_NONE) {
69  // The points cannot be made into a face: either the points do not lie in a plane, or is somehow problematic.
70  return SU_INVALID;
71  }
72  loop_input.m_attached = true;
73  return face;
74 }
75 
76 
77 SUFaceRef Face::copy_reference(const Face& other) {
78  if (other.m_attached || !other) {
79  return other.ref();
80  }
81  // The other face has not been attached to the model, so copy its properties to a new object
82  Loop other_outer_loop = other.outer_loop();
83  std::vector<Point3D> other_outer_points = other_outer_loop.points();
84  LoopInput outer_loop_input = other_outer_loop.loop_input();
85  SUFaceRef new_face = create_face(other_outer_points, outer_loop_input);
86  return new_face;
87 }
88 
89 
90 /*****************************
91 * Constructors / Destructor **
92 ******************************/
94  DrawingElement(SU_INVALID, false)
95 {}
96 
97 
98 Face::Face(std::vector<Point3D>& outer_loop):
99  Face(create_face(outer_loop), false)
100 {}
101 
102 
103 Face::Face(std::vector<Point3D>& outer_loop, LoopInput& loop_input):
104  Face(create_face(outer_loop, loop_input), false)
105 {}
106 
107 
108 Face::Face(SUFaceRef face, bool attached):
109  DrawingElement(SUFaceToDrawingElement(face), attached)
110 {}
111 
112 
113 /** Copy constructor */
114 Face::Face(const Face& other):
115  DrawingElement(other, SUFaceToDrawingElement(copy_reference(other)))
116 {
117  if (!other.m_attached && SUIsValid(other.m_entity)) {
118  /** The code below causes errors as the face has not been attached to an object yet.
119  // Add the inner loops
120  std::vector<Loop> inner_loops = other.inner_loops();
121  std::vector<std::vector<Point3D>> inner_loops_points;
122  inner_loops_points.reserve(inner_loops.size());
123  for (size_t i=0; i < inner_loops.size(); ++i) {
124  std::vector<Point3D> inner_points = inner_loops[i].points();
125  LoopInput inner_loop_input = inner_loops[i].loop_input();
126  add_inner_loop(inner_points, inner_loop_input);
127  }
128  */
129  this->back_material(other.back_material());
130  }
131 }
132 
133 
135  if (!m_attached && SUIsValid(m_entity)) {
136  SUFaceRef face = this->ref();
137  SUResult res = SUFaceRelease(&face);
138  assert(res == SU_ERROR_NONE); _unused(res);
139  }
140 }
141 
142 /******************
143 * Public Methods **
144 *******************/
145 /** Copy assignment operator */
146 Face& Face::operator=(const Face& other) {
147  if (!m_attached && SUIsValid(m_entity)) {
148  SUFaceRef face = this->ref();
149  SUResult res = SUFaceRelease(&face);
150  assert(res == SU_ERROR_NONE); _unused(res);
151  }
152  m_entity = SUFaceToEntity(copy_reference(other));
153  if (!other.m_attached && SUIsValid(other.m_entity)) {
154  this->back_material(other.back_material());
155  // TODO: copy other properties
156  }
158  return *this;
159 }
160 
161 
162 SUFaceRef Face::ref() const { return SUFaceFromEntity(m_entity); }
163 
164 
165 Face::operator SUFaceRef() const { return this->ref();}
166 
167 
168 bool Face::operator!() const {
169  if (SUIsInvalid(m_entity)) {
170  return true;
171  }
172  return false;
173 }
174 
175 
176 double Face::area() const {
177  if (!(*this)) {
178  throw std::logic_error("CW::Face::area(): Face is null");
179  }
180  double area;
181  SUResult res = SUFaceGetArea(this->ref(), &area);
182  assert(res == SU_ERROR_NONE); _unused(res);
183  return area;
184 }
185 
186 
187 void Face::add_inner_loop(std::vector<Point3D>& points, LoopInput &loop_input) {
188  if (!(*this)) {
189  throw std::logic_error("CW::Face::add_inner_loop(): Face is null");
190  }
191  if (points.size() != loop_input.m_edge_num) {
192  throw std::invalid_argument("CW::Face::add_inner_loop(): Unequal number of vertices between given Point3D vector and LoopInput object");
193  }
194  SUResult res = SUFaceAddInnerLoop(this->ref(), points[0], loop_input);
195  if (res == SU_ERROR_INVALID_INPUT) {
196  throw std::invalid_argument("CW::Face::add_inner_loop(): Arguments are invalid");
197  }
198  assert(res == SU_ERROR_NONE); _unused(res);
199 }
200 
201 
202 Material Face::back_material() const {
203  if (!(*this)) {
204  throw std::logic_error("CW::Face::back_material(): Face is null");
205  }
206  SUMaterialRef material = SU_INVALID;
207  SUResult res = SUFaceGetBackMaterial(this->ref(), &material);
208  if (res == SU_ERROR_NO_DATA) {
209  return Material();
210  }
211  assert(res == SU_ERROR_NONE); _unused(res);
212  return Material(material);
213 }
214 
215 
216 Material Face::back_material(const Material& material) {
217  if (!(*this)) {
218  throw std::logic_error("CW::Face::back_material(): Face is null");
219  }
220  SUResult res = SUFaceSetBackMaterial(this->ref(), material.ref());
221  assert(res == SU_ERROR_NONE); _unused(res);
222  return material;
223 }
224 
225 /*
226 * determine if a given Point3d is on the referenced Face. The return value is calculated from this list:
227  PointUnknown (indicates an error),
228  PointInside (point is on the face, not in a hole),
229  PointOnVertex (point touches a vertex),
230  PointOnEdge (point is on an edge),
231  PointOutside (point outside the face or in a hole),
232  PointNotOnPlane (point off the face's plane).
233 * @param SUPoint3D object.
234 * @return FacePointClass enum indicating the status of the point relative to the face.
235 */
236 FacePointClass Face::classify_point(const Point3D& point) {
237  if (!(*this)) {
238  throw std::logic_error("CW::Face::classify_point(): Face is null");
239  }
240  if (!point) {
241  throw std::invalid_argument("CW::Face::classify_point(): Given Point3D object is null");
242  }
243  Loop outer_loop = this->outer_loop();
244  PointLoopClassify outer_class = outer_loop.classify_point(point);
245  switch (outer_class) {
246  case (PointLoopClassify::PointUnknown):
247  return FacePointClass::PointUnknown;
248  case (PointLoopClassify::PointNotOnPlane):
249  return FacePointClass::PointNotOnPlane;
250  case (PointLoopClassify::PointOnEdge):
251  return FacePointClass::PointOnEdge;
252  case (PointLoopClassify::PointOnVertex):
253  return FacePointClass::PointOnVertex;
254  case (PointLoopClassify::PointOutside):
255  return FacePointClass::PointOutside;
256  case (PointLoopClassify::PointInside): {
257  std::vector<Loop> inner_loops = this->inner_loops();
258  for (size_t i=0; i < inner_loops.size(); i++) {
259  PointLoopClassify inner_class = inner_loops[i].classify_point(point);
260  switch (inner_class) {
261  case (PointLoopClassify::PointOnEdge):
262  return FacePointClass::PointOnEdge;
263  case (PointLoopClassify::PointOnVertex):
264  return FacePointClass::PointOnVertex;
265  case (PointLoopClassify::PointOutside):
266  break;
267  case (PointLoopClassify::PointInside):
268  return FacePointClass::PointOutside;
269  break;
270  case (PointLoopClassify::PointNotOnPlane):
271  case (PointLoopClassify::PointUnknown):
272  assert(false); // this should not happen
273  }
274  }
275  return FacePointClass::PointOutside;
276  } break;
277  }
278  return FacePointClass::PointUnknown;
279 }
280 
281 
282 std::vector<Edge> Face::edges() {
283  if (!(*this)) {
284  throw std::logic_error("CW::Face::edges(): Face is null");
285  }
286  std::vector<Edge> total_edges = outer_loop().edges();
287  std::vector<Loop> all_loops = loops();
288  size_t total_edge_num = 0;
289  for (size_t i=0; i < all_loops.size(); i++) {
290  total_edge_num += all_loops[i].size();
291  }
292  total_edges.reserve(total_edge_num);
293  for (size_t i=0; i < all_loops.size(); i++) {
294  std::vector<Edge> loop_edges = all_loops[i].edges();
295  total_edges.insert(total_edges.end(), loop_edges.begin(), loop_edges.end());
296  }
297  return total_edges;
298 }
299 
300 /*
301 // TODO
302 UVHelper Face::get_UVHelper(bool front, bool back, TextureWriter tex_writer) {
303  //SUUVHelperRef uv_helper = SU_INVALID;
304  UVHelper uv_helper{};
305  SUFaceGetUVHelper(m_face, front, back, tex_writer, uv_helper);
306  return uv_helper;
307 }
308 */
309 
310 
311 /** NOT POSSIBLE WITH C API - @see class MaterialInput **/
312 // Vector3D Face::get_texture_projection(const bool frontside) const {}
313 
314 
315 std::vector<Loop> Face::inner_loops() const {
316  if (!(*this)) {
317  throw std::logic_error("CW::Face::inner_loops(): Face is null");
318  }
319  size_t num_loops = 0;
320  SUFaceGetNumInnerLoops(this->ref(), &num_loops);
321  std::vector<SULoopRef> inner_loops(num_loops, SU_INVALID);
322  SUFaceGetInnerLoops(this->ref(), num_loops, inner_loops.data(), &num_loops);
323  std::vector<Loop> loops(num_loops);
324  std::transform(inner_loops.begin(), inner_loops.end(), loops.begin(),
325  [](const SULoopRef& value){
326  return Loop(value);
327  });
328  return loops;
329 }
330 
331 
332 std::vector<Loop> Face::loops() const {
333  if (!(*this)) {
334  throw std::logic_error("CW::Face::loops(): Face is null");
335  }
336  std::vector<Loop> all_loops;
337  all_loops.push_back(outer_loop());
338  std::vector<Loop> inner_ls = inner_loops();
339  all_loops.reserve(all_loops.size() + inner_ls.size());
340  all_loops.insert(all_loops.end(), inner_ls.begin(), inner_ls.end());
341  return all_loops;
342 }
343 
344 /*
345 // TODO another day.
346 PolygonMesh mesh();
347 */
348 
349 
350 Vector3D Face::normal() const {
351  if (!(*this)) {
352  throw std::logic_error("CW::Face::normal(): Face is null");
353  }
354  Plane3D c_plane = plane();
355  return c_plane.normal();
356 }
357 
358 
359 Loop Face::outer_loop() const {
360  if (!(*this)) {
361  throw std::logic_error("CW::Face::outer_loop(): Face is null");
362  }
363  SULoopRef lp = SU_INVALID;
364  SUResult res = SUFaceGetOuterLoop(this->ref(), &lp);
365  assert(res == SU_ERROR_NONE); _unused(res);
366  return Loop(lp);
367 }
368 
369 
370 Plane3D Face::plane() const {
371  if (!(*this)) {
372  throw std::logic_error("CW::Face::plane(): Face is null");
373  }
374  SUPlane3D plane = SU_INVALID;
375  SUFaceGetPlane(this->ref(), &plane);
376  return Plane3D(plane);
377 }
378 
379 
380 /** NOT POSSIBLE WITH C API - @see class MaterialInput **/
381 //bool Face::position_material(const Material& material, const std::vector<Point3D>& pt_array, bool o_front) {}
382 
383 
385  if (!(*this)) {
386  throw std::logic_error("CW::Face::reverse(): Face is null");
387  }
388  SUResult res = SUFaceReverse(this->ref());
389  assert(res == SU_ERROR_NONE); _unused(res);
390  return *this;
391 }
392 
393 
394 /*
395 * Sets the texture projection direction.
396 * @param SUVector3D object representing the direction of the projection. Or bool true to remove texture projection.
397 * @param bool true for front side, false for back side.
398 * @return true on success
399 */
400 /** NOT POSSIBLE WITH C API - @see class MaterialInput **/
401 /**
402 bool Face::set_texture_projection(const Vector3D& vector, bool frontside) {}
403 
404 bool Face::set_texture_projection(bool remove, bool frontside) {}
405 */
406 
407 
408 std::vector<Vertex> Face::vertices() const {
409  if (!(*this)) {
410  throw std::logic_error("CW::Face::vertices(): Face is null");
411  }
412  size_t num_vertices = 0;
413  SUFaceGetNumVertices(this->ref(), &num_vertices);
414  std::vector<SUVertexRef> vertex_refs(num_vertices, SU_INVALID);
415  SUFaceGetVertices(this->ref(), num_vertices, vertex_refs.data(), &num_vertices);
416  std::vector<Vertex> vertices(num_vertices);
417  std::transform(vertex_refs.begin(), vertex_refs.end(), vertices.begin(),
418  [](const SUVertexRef& value){
419  return Vertex(value);
420  });
421  return vertices;
422 }
423 
424 } /* namespace CW */
Vector3D normal() const
Definition: Geometry.cpp:834
bool m_attached
Indicates whether the Entity has been attached to a model.
Definition: Entity.hpp:64
bool attached() const
Returns true if the entity is attached to another object.
Definition: Entity.cpp:100
void add_inner_loop(std::vector< Point3D > &points, LoopInput &loop_input)
Definition: Face.cpp:187
std::vector< Loop > inner_loops() const
Definition: Face.cpp:315
Face & reverse()
Definition: Face.cpp:384
Face()
Definition: Face.cpp:93
DrawingElement & operator=(const DrawingElement &other)
SUEntityRef m_entity
The C SUEntityRef that this class wraps.
Definition: Entity.hpp:59
~Face()
Definition: Face.cpp:134
Face & operator=(const Face &other)
Definition: Face.cpp:146
PointLoopClassify classify_point(const Point3D &point) const
Definition: Loop.cpp:145
Definition: Color.hpp:34
std::vector< Vertex > vertices() const
Definition: Face.cpp:408
bool operator!() const
Definition: Face.cpp:168