SUAPI-CppWrapper
C++WrapperforSketchUpCAPI
Entities.cpp
1 //
2 // Entities.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 
33 #include "SUAPI-CppWrapper/model/Entities.hpp"
34 
35 #include "SUAPI-CppWrapper/model/GeometryInput.hpp"
36 #include "SUAPI-CppWrapper/model/Vertex.hpp"
37 #include "SUAPI-CppWrapper/model/Loop.hpp"
38 #include "SUAPI-CppWrapper/Transformation.hpp"
39 #include "SUAPI-CppWrapper/model/ComponentDefinition.hpp"
40 #include "SUAPI-CppWrapper/model/ComponentInstance.hpp"
41 #include "SUAPI-CppWrapper/model/Group.hpp"
42 #include "SUAPI-CppWrapper/model/Face.hpp"
43 #include "SUAPI-CppWrapper/model/Edge.hpp"
44 #include "SUAPI-CppWrapper/model/Model.hpp"
45 #include "SUAPI-CppWrapper/model/Material.hpp"
46 
47 namespace CW {
48 
49 
50 Entities::Entities(SUEntitiesRef entities, const SUModelRef model):
51  m_entities(entities),
52  m_model(model)
53 {}
54 
56  m_entities(SU_INVALID),
57  m_model(SU_INVALID)
58 {}
59 
60 
61 std::vector<Face> Entities::faces() const {
62  if (!SUIsValid(m_entities)) {
63  throw std::logic_error("CW::Entities::faces(): Entities is null");
64  }
65  size_t count = 0;
66  SUResult res = SUEntitiesGetNumFaces(m_entities, &count);
67  assert(res == SU_ERROR_NONE);
68  if (count == 0) {
69  return std::vector<Face>(0);
70  }
71  std::vector<SUFaceRef> face_refs(count, SU_INVALID);
72  res = SUEntitiesGetFaces(m_entities, count, face_refs.data(), &count);
73  assert(res == SU_ERROR_NONE); _unused(res);
74  std::vector<Face> faces(count);
75  std::transform(face_refs.begin(), face_refs.end(), faces.begin(),
76  [](const SUFaceRef& value) {
77  return Face(value);
78  });
79  return faces;
80 }
81 
82 
83 std::vector<Edge> Entities::edges(bool stray_only) const {
84  if (!SUIsValid(m_entities)) {
85  throw std::logic_error("CW::Entities::edges(): Entities is null");
86  }
87  size_t count = 0;
88  SUResult res = SUEntitiesGetNumEdges(m_entities, stray_only, &count);
89  assert(res == SU_ERROR_NONE);
90  if (count == 0) {
91  return std::vector<Edge>(0);
92  }
93  std::vector<SUEdgeRef> edge_refs(count, SU_INVALID);
94  res = SUEntitiesGetEdges(m_entities, stray_only, count, edge_refs.data(), &count);
95  assert(res == SU_ERROR_NONE); _unused(res);
96  std::vector<Edge> edges(count);
97  std::transform(edge_refs.begin(), edge_refs.end(), edges.begin(),
98  [](const SUEdgeRef& value) {
99  return Edge(value);
100  });
101  return edges;
102 }
103 
104 
105 std::vector<ComponentInstance> Entities::instances() const {
106  if (!SUIsValid(m_entities)) {
107  throw std::logic_error("CW::Entities::instances(): Entities is null");
108  }
109  size_t count = 0;
110  SUResult res = SUEntitiesGetNumInstances(m_entities, &count);
111  assert(res == SU_ERROR_NONE);
112  if (count == 0) {
113  return std::vector<ComponentInstance>{};
114  }
115  std::vector<SUComponentInstanceRef> instance_refs(count, SU_INVALID);
116  res = SUEntitiesGetInstances(m_entities, count, instance_refs.data(), &count);
117  assert(res == SU_ERROR_NONE); _unused(res);
118  std::vector<ComponentInstance> instances(count);
119  std::transform(instance_refs.begin(), instance_refs.end(), instances.begin(),
120  [](const SUComponentInstanceRef& value) {
121  return ComponentInstance(value);
122  });;
123  return instances;
124 }
125 
126 
127 std::vector<Group> Entities::groups() const {
128  if (!SUIsValid(m_entities)) {
129  throw std::logic_error("CW::Entities::groups(): Entities is null");
130  }
131  size_t count = 0;
132  SUResult res = SUEntitiesGetNumGroups(m_entities, &count);
133  assert(res == SU_ERROR_NONE);
134  if (count == 0) {
135  return std::vector<Group>(0);
136  }
137  std::vector<SUGroupRef> group_refs(count);
138  res = SUEntitiesGetGroups(m_entities, count, &group_refs[0], &count);
139  assert(res == SU_ERROR_NONE); _unused(res);
140  std::vector<Group> groups;
141  groups.reserve(count);
142  for (size_t i=0; i < group_refs.size(); ++i) {
143  groups.push_back(Group(group_refs[i], true));
144  }
145  return groups;
146 }
147 
148 
150  if (!SUIsValid(m_entities)) {
151  throw std::logic_error("CW::Entities::groups(): Entities is null");
152  }
153  SUBoundingBox3D box = SU_INVALID;
154  SUResult res = SUEntitiesGetBoundingBox(m_entities, &box);
155  assert(res == SU_ERROR_NONE); _unused(res);
156  return BoundingBox3D(box);
157 }
158 
159 
160 size_t Entities::size() const {
161  if (!SUIsValid(m_entities)) {
162  throw std::logic_error("CW::Entities::size(): Entities is null");
163  }
164  size_t total_count = 0;
165  size_t count = 0;
166  SUResult res = SUEntitiesGetNumFaces(m_entities, &count);
167  assert(res == SU_ERROR_NONE);
168  total_count += count;
169  count = 0;
170  res = SUEntitiesGetNumEdges(m_entities, true, &count);
171  assert(res == SU_ERROR_NONE);
172  total_count += count;
173  count = 0;
174  res = SUEntitiesGetNumInstances(m_entities, &count);
175  assert(res == SU_ERROR_NONE);
176  total_count += count;
177  count = 0;
178  res = SUEntitiesGetNumGroups(m_entities, &count);
179  assert(res == SU_ERROR_NONE); _unused(res);
180  total_count += count;
181  return total_count;
182 }
183 
184 
185 void Entities::add(const Entities& other) {
186  if (!SUIsValid(m_entities) || !SUIsValid(other.m_entities)) {
187  throw std::logic_error("CW::Entities::add(): Entities is null");
188  }
189  GeometryInput geom_input(m_model);
190  geom_input.add_faces(other.faces());
191  geom_input.add_edges(other.edges());
192  this->fill(geom_input);
193  std::vector<ComponentInstance> instances = other.instances();
194  for (size_t i=0; i < instances.size(); ++i) {
195  this->add_instance(instances[i].definition(), instances[i].transformation(), instances[i].name());
196  }
197  std::vector<Group> other_groups = other.groups();
198  for (size_t i=0; i < other_groups.size(); ++i) {
199  Group new_group = this->add_group();
200  new_group.entities().add(other_groups[i].entities());
201  new_group.transformation(other_groups[i].transformation());
202  new_group.name(other_groups[i].name());
203  }
204  // TODO: other geometry types need to be added.
205 }
206 
207 
208 
209 SUResult Entities::fill(GeometryInput &geom_input) {
210  if (!SUIsValid(m_entities)) {
211  throw std::logic_error("CW::Entities::fill(): Entities is null");
212  }
213  // Check geom_input is not empty.
214  if (geom_input.empty()) {
215  return SU_ERROR_NONE;
216  }
217 
218  // For the indexes of the GeometryInputRef to make sense after we fill the Entities object with its contents, we need to know how many of each entitity currently exists in the Entities object
219  size_t num_faces_before = 0;
220  SUResult res = SUEntitiesGetNumFaces(m_entities, &num_faces_before);
221  assert(res == SU_ERROR_NONE); _unused(res);
222 
223  SUResult fill_res = SUEntitiesFill(m_entities, geom_input.m_geometry_input, true);
224  assert(fill_res == SU_ERROR_NONE); _unused(fill_res);
225  /**
226  // Now add other data that SUEntitiesFill cannot add to the entities.
227  // Apply properties to Faces
228  // TODO: there is an assumption that the faces added to an Entities object is added in sequence, according to the index number. So that (num_faces_before + face_index_of_geom_input) correspond to the Face number in the Entities object. This needs to be tested.
229  size_t num_faces_after = 0;
230  res = SUEntitiesGetNumFaces(m_entities, &num_faces_after);
231  assert(res == SU_ERROR_NONE);
232  std::vector<std::pair<size_t, Face>> faces_to_add = geom_input.faces();
233  // If all of the faces in the geom_input were not added, it will not be possible to find the added face by looking at its index.
234  assert((num_faces_after-num_faces_before) == geom_input.num_faces());
235  // Copy any attributes to the added faces
236  std::vector<Face> faces_after = this->faces();
237  for (size_t i=0; i < faces_to_add.size(); ++i) {
238  assert(this->model().material_exists(faces_to_add[i].second.material()));
239  size_t new_face_index = faces_to_add[i].first;
240  size_t after_face_index = num_faces_before + new_face_index;
241  faces_after[after_face_index].copy_attributes_from(faces_to_add[i].second);
242  // Set attributes for the edges that bound the face.
243  std::vector<Loop> loops = faces_to_add[i].second.loops();
244  std::vector<Loop> new_loops = faces_after[after_face_index].loops();
245  for (size_t j=0; j < loops.size(); ++j) {
246  std::vector<Edge> old_edges = loops[j].edges();
247  std::vector<Edge> new_edges = new_loops[j].edges();
248  // If there are more new edges, then it means that some edges have been split during a merging operation. The trick to find these is to see if the end location of the new and old edges match.
249  size_t new_edge_index = 0;
250  size_t old_edge_index = 0;
251  while( old_edge_index < old_edges.size()) {
252  do {
253  new_edges[new_edge_index].copy_attributes_from(old_edges[old_edge_index]);
254  ++new_edge_index;
255  }
256  while ((old_edges.size() < new_edges.size()) &&
257  (new_edge_index < new_edges.size()) &&
258  (new_edges[new_edge_index-1].end().position() != old_edges[old_edge_index+1].end().position()));
259  ++old_edge_index;
260  }
261  }
262  }
263  */
264  return fill_res;
265 }
266 
267 std::vector<Face> Entities::add_faces(std::vector<Face>& faces) {
268  if (!SUIsValid(m_entities)) {
269  throw std::logic_error("CW::Entities::add_faces(): Entities is null");
270  }
271  std::vector<SUFaceRef> refs(faces.size(), SU_INVALID);
272  std::transform(faces.begin(), faces.end(), refs.begin(), [](const CW::Face& face) {return face.ref(); });
273 
274  SUResult res = SUEntitiesAddFaces(m_entities, refs.size(), refs.data());
275  assert(res == SU_ERROR_NONE); _unused(res);
276 
277  // Transfer ownership of each face
278  for (auto& face : faces)
279  face.attached(true);
280 
281  return faces;
282 }
283 
284 std::vector<Edge> Entities::add_edges(std::vector<Edge>& edges) {
285  if (!SUIsValid(m_entities)) {
286  throw std::logic_error("CW::Entities::add_edges(): Entities is null");
287  }
288  std::vector<SUEdgeRef> refs(edges.size(), SU_INVALID);
289  std::transform(edges.begin(), edges.end(), refs.begin(), [](const CW::Edge& edge) {return edge.ref(); });
290 
291  SUResult res = SUEntitiesAddEdges(m_entities, refs.size(), refs.data());
292  assert(res == SU_ERROR_NONE); _unused(res);
293 
294  // Transfer ownership of each edge
295  for (auto& edge : edges)
296  edge.attached(true);
297 
298  return edges;
299 }
300 
301 
302 Edge Entities::add_edge(Edge& edge) {
303  if (!SUIsValid(m_entities)) {
304  throw std::logic_error("CW::Entities::add_edge(): Entities is null");
305  }
306  SUEdgeRef edge_ref = edge.ref();
307  SUResult res = SUEntitiesAddEdges(m_entities, 1, &edge_ref);
308  assert(res == SU_ERROR_NONE); _unused(res);
309  edge.attached(true);
310  return edge;
311 }
312 
313 
314 void Entities::add_instance(ComponentInstance& instance) {
315  if (!SUIsValid(m_entities)) {
316  throw std::logic_error("CW::Entities::add_instance(): Entities is null");
317  }
318  if (!instance) {
319  throw std::invalid_argument("CW::Entities::add_instance(): ComponentInstance argument is invalid");
320  }
321  SUResult res = SUEntitiesAddInstance(m_entities, instance, nullptr);
322  assert(res == SU_ERROR_NONE); _unused(res);
323  instance.attached(true);
324 }
325 
326 
327 ComponentInstance Entities::add_instance(const ComponentDefinition& definition, const Transformation& transformation, const String& name){
328  if (!SUIsValid(m_entities)) {
329  throw std::logic_error("CW::Entities::add_instance(): Entities is null");
330  }
331  if (!definition) {
332  throw std::invalid_argument("CW::Entities::add_instance(): ComponentDefinition argument is invalid");
333  }
334  SUComponentInstanceRef instance = SU_INVALID;
335  SUResult res = SUComponentDefinitionCreateInstance(definition.ref(), &instance);
336  assert(res == SU_ERROR_NONE);
337  SUTransformation transform = transformation.ref();
338  res = SUComponentInstanceSetTransform(instance, &transform);
339  assert(res == SU_ERROR_NONE);
340  if (name.empty()) {
341  res = SUEntitiesAddInstance(m_entities, instance, NULL);
342  }
343  else {
344  SUStringRef name_ref = name.ref();
345  res = SUEntitiesAddInstance(m_entities, instance, &name_ref);
346  }
347  assert(res == SU_ERROR_NONE); _unused(res);
348  return ComponentInstance(instance, true);
349 }
350 
351 
352 // TODO: add_group needs to be refined
353 
354 Group Entities::add_group(const ComponentDefinition& definition, const Transformation& transformation) {
355  if (!SUIsValid(m_entities)) {
356  throw std::logic_error("CW::Entities::add_group(): Entities is null");
357  }
358  if (!definition) {
359  throw std::invalid_argument("CW::Entities::add_group(): ComponentDefinition argument is invalid");
360  }
361  if (!definition.is_group()) {
362  throw std::invalid_argument("CW::Entities::add_group(): ComponentDefinition given is not a group");
363  }
364  // Groups cannot be created with a component definition and transformation objects. Instead, the geometry must be copied in to a new Entities object in the group.
365  Group new_group = this->add_group();
366  Entities group_entities = new_group.entities();
367  Entities def_entities = definition.entities();
368  // Add geometry one by one to Geometry input.
369  GeometryInput geom_input(m_model);
370  std::vector<Face> def_faces = def_entities.faces();
371  for (size_t i=0; i < def_faces.size(); ++i) {
372  geom_input.add_face(def_faces[i]);
373  }
374  std::vector<Edge> def_edges = def_entities.edges(true);
375  for (size_t i=0; i < def_edges.size(); ++i) {
376  group_entities.add_edge(def_edges[i]);
377  }
378  group_entities.fill(geom_input);
379  // Also add instances and groups
380  std::vector<Group> def_groups = def_entities.groups();
381  for (size_t i=0; i < def_groups.size(); ++i) {
382  group_entities.add_group(def_groups[i].definition() , def_groups[i].transformation());
383  }
384  std::vector<ComponentInstance> def_instances = def_entities.instances();
385  for (size_t i=0; i < def_instances.size(); ++i) {
386  group_entities.add_instance(def_instances[i].definition(), def_instances[i].transformation());
387  }
388 
389  // TODO: add other entities to the group (construction lines, curves, etc)
390  return new_group;
391 
392  // TODO: the way groups are implemented are a problem. Come back to this.
393  //SUResult res = SUComponentDefinitionCreateGroup(definition.ref(), &group);
394  //assert(res == SU_ERROR_NONE); _unused(res);
395  //SUTransformation transform = transformation.ref();
396  //res = SUComponentInstanceSetTransform(instance, &transform);
397 }
398 
399 Group Entities::add_group() {
400  if (!SUIsValid(m_entities)) {
401  throw std::logic_error("CW::Entities::add_group(): Entities is null");
402  }
403  SUGroupRef group = SU_INVALID;
404  SUResult res = SUGroupCreate(&group);
405  assert(res == SU_ERROR_NONE);
406  // Add group to the entities object before populating it.
407  res = SUEntitiesAddGroup(m_entities, group);
408  assert(res == SU_ERROR_NONE); _unused(res);
409  return Group(group);
410 }
411 
412 
413 bool Entities::transform_entities(std::vector<Entity>& elems, const Transformation& transform) {
414  if (!SUIsValid(m_entities)) {
415  throw std::logic_error("CW::Entities::transform_entities(): Entities is null");
416  }
417  SUTransformation trans_ref = transform.ref();
418  SUResult res = SUEntitiesTransform(m_entities, elems.size(), elems[0], &trans_ref);
419  assert(res == SU_ERROR_NONE || res == SU_ERROR_GENERIC); _unused(res);
420  if (res == SU_ERROR_UNSUPPORTED) {
421  throw std::invalid_argument("CW::Entities::transform_entities(): One of the elements given in the Entity vector is not contained by this Entities object.");
422  }
423  else if (SU_ERROR_GENERIC) {
424  return false;
425  }
426  return true;
427 }
428 
429 
430 bool Entities::transform_entities(std::vector<Entity>& elems, std::vector<Transformation>& transforms) {
431  if (!SUIsValid(m_entities)) {
432  throw std::logic_error("CW::Entities::transform_entities(): Entities is null");
433  }
434  if (elems.size() != transforms.size()) {
435  throw std::invalid_argument("CW::Entities::transform_entities(): different number of elements to transformation objects given - the same number must be given.");
436  }
437  assert(elems.size() == transforms.size());
438  SUResult res = SUEntitiesTransformMultiple(m_entities, elems.size(), elems[0], transforms[0]);
439  if (res == SU_ERROR_UNSUPPORTED) {
440  throw std::invalid_argument("CW::Entities::transform_entities(): One of the elements given in the Entity vector is not contained by this Entities object.");
441  }
442  else if (SU_ERROR_GENERIC) {
443  return false;
444  }
445  return true;
446 }
447 
448 
450  if (SUIsInvalid(m_model)) {
451  return Model();
452  }
453  return Model(m_model, false);
454 }
455 
456 
457 Entities::operator SUEntitiesRef() {
458  return m_entities;
459 }
460 
461 } /* namespace CW */
Model model() const
Definition: Entities.cpp:449
SUComponentDefinitionRef ref() const
bool transform_entities(std::vector< Entity > &elems, const Transformation &transform)
Definition: Entities.cpp:413
size_t size() const
Definition: Entities.cpp:160
bool attached() const
Returns true if the entity is attached to another object.
Definition: Entity.cpp:100
size_t add_face(const Face &face, bool copy_material_layer=true)
SUResult fill(GeometryInput &geom_input)
Definition: Entities.cpp:209
Transformation transformation() const
Definition: Group.cpp:160
SUEdgeRef ref() const
Definition: Edge.cpp:135
String name() const
Definition: Group.cpp:138
bool empty() const
Definition: String.cpp:180
bool empty() const
BoundingBox3D bounding_box() const
Definition: Entities.cpp:149
Definition: Color.hpp:34
void add(const Entities &other)
Definition: Entities.cpp:185
Entities entities() const
Definition: Group.cpp:128