SUAPI-CppWrapper
C++WrapperforSketchUpCAPI
Model.cpp
1 //
2 // Model.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 "SUAPI-CppWrapper/model/Model.hpp"
32 
33 #include <cassert>
34 
35 #include "SUAPI-CppWrapper/model/Layer.hpp"
36 #include "SUAPI-CppWrapper/model/Axes.hpp"
37 #include "SUAPI-CppWrapper/model/Entities.hpp"
38 //#include "SUAPI-CppWrapper/Behavior.hpp"
39 //#include "SUAPI-CppWrapper/model/Classifications.hpp"
40 #include "SUAPI-CppWrapper/model/ComponentDefinition.hpp"
41 #include "SUAPI-CppWrapper/model/InstancePath.hpp"
42 #include "SUAPI-CppWrapper/model/Material.hpp"
43 #include "SUAPI-CppWrapper/model/AttributeDictionary.hpp"
44 #include "SUAPI-CppWrapper/model/TypedValue.hpp"
45 #include "SUAPI-CppWrapper/model/OptionsManager.hpp"
46 #include "SUAPI-CppWrapper/model/RenderingOptions.hpp"
47 #include "SUAPI-CppWrapper/model/ShadowInfo.hpp"
48 
49 
50 namespace CW {
51 
52 
53 SUModelRef Model::create_model() {
54  SUModelRef model = SU_INVALID;
55  SUResult res = SUModelCreate(&model);
56  assert(res == SU_ERROR_NONE); _unused(res);
57  return model;
58 }
59 
60 
61 Model::Model():
62  m_model(create_model()),
63  m_release_on_destroy(true)
64 {}
65 
66 
67 Model::Model(SUModelRef model_ref, bool release_on_destroy):
68  m_model(model_ref),
69  m_release_on_destroy(release_on_destroy)
70 {}
71 
72 Model::Model(std::string file_path):
73  m_model(SU_INVALID),
74  m_release_on_destroy(true),
75  m_result(SUModelCreateFromFile(&m_model, file_path.c_str()))
76 {}
77 
78 Model::Model(const Model& other):
79  m_model(other.m_model),
80  m_release_on_destroy(other.m_release_on_destroy)
81 {}
82 
83 Model::~Model() {
84  if (m_release_on_destroy && SUIsValid(m_model)) {
85  SUResult res = SUModelRelease(&m_model);
86  assert(res == SU_ERROR_NONE); _unused(res);
87  }
88 }
89 
90 
91 SUModelRef Model::ref() const {
92  return m_model;
93 }
94 
95 
96 Model::operator SUModelRef() const {
97  return ref();
98 }
99 
100 
101 Model::operator SUModelRef*() {
102  return &m_model;
103 }
104 
105 
106 Model::operator bool() const {
107  if (m_result == SU_ERROR_NONE) {
108  return true;
109  }
110  return false;
111 }
112 
113 
114 bool Model::operator!() const {
115  return !bool(*this);
116 }
117 
118 std::string Model::version_string() const
119 {
120  int major = 0, minor = 0, build = 0;
121  SUResult res = SUModelGetVersion(m_model, &major, &minor, &build);
122  assert(res == SU_ERROR_NONE); _unused(res);
123  std::string version = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(build);
124  return version;
125 }
126 
127 
128 Layer Model::active_layer() const {
129  if(!(*this)) {
130  throw std::logic_error("CW::Model::active_layer(): Model is null");
131  }
132  SULayerRef layer = SU_INVALID;
133  SUResult res = SUModelGetDefaultLayer(m_model, &layer);
134  assert(res == SU_ERROR_NONE); _unused(res);
135  return Layer(layer);
136 }
137 
138 /*
139 * Sets the active layer of the model.
140 * @param default_layer the Layer object to be the active layer on first opening the model
141 * @return status true if succsessful
142  * TODO: default layer cannot be set through API
143 */
144 //bool active_layer(Layer default_layer) {}
145 
146 
148  if(!(*this)) {
149  throw std::logic_error("CW::Model::add_definition(): Model is null");
150  }
151  std::vector<ComponentDefinition> defs = {definition};
152  return add_definitions(defs);
153 }
154 
155 
156 bool Model::add_definitions(std::vector<ComponentDefinition>& definitions) {
157  if(!(*this)) {
158  throw std::logic_error("CW::Model::add_definitions(): Model is null");
159  }
160  std::vector<SUComponentDefinitionRef> defs(definitions.size(), SU_INVALID);
161  std::transform(definitions.begin(), definitions.end(), defs.begin(),
162  [](const ComponentDefinition& definition) {
163  return definition.ref();
164  }
165  );
166  SUResult res = SUModelAddComponentDefinitions(m_model, definitions.size(), defs.data());
167  if (res == SU_ERROR_NONE) {
168  return true;
169  }
170  return false;
171 }
172 
173 
174 std::vector<AttributeDictionary> Model::attribute_dictionaries() const {
175  if(!(*this)) {
176  throw std::logic_error("CW::Model::attribute_dictionaries(): Model is null");
177  }
178  size_t count = 0;
179  SUResult res = SUModelGetNumAttributeDictionaries(m_model, &count);
180  assert(res == SU_ERROR_NONE);
181  std::vector<SUAttributeDictionaryRef> dict_refs(count, SU_INVALID);
182  res = SUModelGetAttributeDictionaries(m_model, count, dict_refs.data(), &count);
183  assert(res == SU_ERROR_NONE); _unused(res);
184  std::vector<AttributeDictionary> dicts(count);
185  std::transform(dict_refs.begin(), dict_refs.end(), dicts.begin(),
186  [](const SUAttributeDictionaryRef& value){
187  return AttributeDictionary(value);
188  });
189  return dicts;
190 }
191 
192 
193 AttributeDictionary Model::attribute_dictionary(const std::string& dict_name) const {
194  if(!(*this)) {
195  throw std::logic_error("CW::Model::attribute_dictionary(): Model is null");
196  }
197  SUAttributeDictionaryRef dict = SU_INVALID;
198  SUResult res = SUModelGetAttributeDictionary(m_model, dict_name.c_str(), &dict);
199  assert(res == SU_ERROR_NONE); _unused(res);
200  return AttributeDictionary(dict);
201 }
202 
203 
204 
205 Axes Model::axes() const {
206  if(!(*this)) {
207  throw std::logic_error("CW::Model::axes(): Model is null");
208  }
209  SUAxesRef axes = SU_INVALID;
210  SUResult res = SUModelGetAxes(m_model, &axes);
211  assert(res == SU_ERROR_NONE); _unused(res);
212  return Axes(axes);
213 }
214 
215 
216 //Behavior behavior(); // TODO: this may not be possible to retrieve
217 
218 /*
219 Classifications Model::classifications() const {
220  if(!(*this)) {
221  throw std::logic_error("CW::Model::classifications(): Model is null");
222  }
223  SUClassificationsRef classifications = SU_INVALID;
224  SUResult res = SUModelGetClassifications(m_model, &classifications);
225  assert(res == SU_ERROR_NONE);
226  return Classifications(classifications);
227 }
228 */
229 
230 /*
231 * Returns the description attached to this model.
232 * @return description string object.
233 */
234 //std::string description();
235 //bool description(std::string description_string);
236 
237 
238 std::vector<ComponentDefinition> Model::definitions() const {
239  if(!(*this)) {
240  throw std::logic_error("CW::Model::definitions(): Model is null");
241  }
242  size_t count = 0;
243  SUResult res = SUModelGetNumComponentDefinitions(m_model, &count);
244  assert(res == SU_ERROR_NONE);
245  std::vector<SUComponentDefinitionRef> def_refs(count, SU_INVALID);
246  res = SUModelGetComponentDefinitions(m_model, count, def_refs.data(), &count);
247  assert(res == SU_ERROR_NONE); _unused(res);
248  std::vector<ComponentDefinition> defs(count);
249  std::transform(def_refs.begin(), def_refs.end(), defs.begin(),
250  [](const SUComponentDefinitionRef& value){
251  return ComponentDefinition(value);
252  });
253  return defs;
254 }
255 
256 
257 std::vector<ComponentDefinition> Model::group_definitions() const {
258  if(!(*this)) {
259  throw std::logic_error("CW::Model::group_definitions(): Model is null");
260  }
261  size_t count = 0;
262  SUResult res = SUModelGetNumGroupDefinitions(m_model, &count);
263  assert(res == SU_ERROR_NONE);
264  std::vector<SUComponentDefinitionRef> def_refs(count, SU_INVALID);
265  res = SUModelGetGroupDefinitions(m_model, count, def_refs.data(), &count);
266  assert(res == SU_ERROR_NONE); _unused(res);
267  std::vector<ComponentDefinition> defs(count);
268  std::transform(def_refs.begin(), def_refs.end(), defs.begin(),
269  [](const SUComponentDefinitionRef& value){
270  return ComponentDefinition(value);
271  });
272  return defs;
273 }
274 
275 
276 InstancePath Model::instance_path(const String& persistent_id) const {
277  SUInstancePathRef instance_path_ref = SU_INVALID;
278  SUResult res = SUInstancePathCreate(&instance_path_ref);
279  assert(res == SU_ERROR_NONE);
280  res = SUModelGetInstancePathByPid(m_model, persistent_id.ref(), &instance_path_ref);
281  if (res == SU_ERROR_GENERIC) {
282  // Probably passed an empty or invalid string - return an empty instance path
283  return InstancePath();
284  }
285  assert(res == SU_ERROR_NONE); _unused(res);
286  return InstancePath(instance_path_ref);
287 }
288 
289 
290 Entities Model::entities() const {
291  if(!(*this)) {
292  throw std::logic_error("CW::Model::entities(): Model is null");
293  }
294  SUEntitiesRef entities = SU_INVALID;
295  SUResult res = SUModelGetEntities(m_model, &entities);
296  assert(res == SU_ERROR_NONE); _unused(res);
297  return Entities(entities, m_model);
298 }
299 
300 
301 //find_entity_by_id(); // TODO can this be done?
302 
303 // TODO - build Location class before this method
304 /*
305 bool Model::georeferenced() const {
306  SULocationRef loc = SU_INVALID;
307  SUResult res = SUModelGetLocation(m_model, &loc);
308  assert(res == SU_ERROR_NONE);
309  Location location(loc);
310  if (loc) {
311  return true;
312  }
313  return false;
314 }
315 */
316 
317 
318 TypedValue Model::get_attribute(const AttributeDictionary& dict, const std::string& key, const TypedValue& default_value) const {
319  if (!(*this)) {
320  throw std::logic_error("CW::Model::get_attribute(): Model is null");
321  }
322  return dict.get_attribute(key, default_value);
323 }
324 
325 TypedValue Model::get_attribute(const std::string& dict_name, const std::string& key, const TypedValue& default_value) const {
326  if (!(*this)) {
327  throw std::logic_error("CW::Model::get_attribute(): Model is null");
328  }
329  AttributeDictionary dictionary = attribute_dictionary(dict_name);
330  return get_attribute(dictionary, key, default_value);
331 }
332 
333 
334 /*
335 * Returns the GUID of the model.
336 */
337 //guid();
338 
339 /*
340 * Returns the list of layers in the model.
341 * @return layers a vector array of Layer objects in the model.
342 */
343 std::vector<Layer> Model::layers() const {
344  if (!(*this)) {
345  throw std::logic_error("CW::Model::layers(): Model is null");
346  }
347  size_t count = 0;
348  SUResult res = SUModelGetNumLayers(m_model, &count);
349  assert(res == SU_ERROR_NONE);
350  std::vector<SULayerRef> layer_refs(count, SU_INVALID);
351  res = SUModelGetLayers(m_model, count, layer_refs.data(), &count);
352  assert(res == SU_ERROR_NONE); _unused(res);
353  std::vector<Layer> layers(count);
354  std::transform(layer_refs.begin(), layer_refs.end(), layers.begin(),
355  [](const SULayerRef& value){
356  return Layer(value);
357  });
358  return layers;
359 }
360 
361 
362 void Model::add_layers(std::vector<Layer>& layers) {
363  for (size_t i=0; i < layers.size(); i++) {
364  // Check that each material is not attached to another model
365  if (layers[i].attached()) {
366  throw std::invalid_argument("CW::Model::add_layers(): At least one of the Layer objects passed is attached to another model. Use Layer::copy() to create a new unattached Layer object and try again.");
367  }
368  }
369  std::vector<SULayerRef> layer_refs(layers.size(), SU_INVALID);
370  std::transform(layers.begin(), layers.end(), layer_refs.begin(),
371  [](const Layer& value){
372  return value.ref();
373  });
374  SUResult res = SUModelAddLayers(m_model, layers.size(), layer_refs.data());
375  assert(res == SU_ERROR_NONE); _unused(res);
376  for (size_t i=0; i < layers.size(); i++) {
377  layers[i].attached(true);
378  }
379 }
380 
381 
382 bool Model::layer_exists(const Layer& layer) const {
383  std::vector<Layer> layers = this->layers();
384  for (auto& lay : layers) {
385  if (lay == layer) {
386  return true;
387  }
388  }
389  return false;
390 }
391 
392 /*
393 * Returns the Location object of the model
394 * @return location Location object. If no location has been assigned to the model, the Location object returned will be invalid.
395 */
396 // Location location();
397 
398 
399 std::vector<Material> Model::materials() const {
400  if (!(*this)) {
401  throw std::logic_error("CW::Model::materials(): Model is null");
402  }
403  size_t count = 0;
404  SUResult res = SUModelGetNumMaterials(m_model, &count);
405  assert(res == SU_ERROR_NONE);
406  if (count == 0) {
407  return std::vector<Material> {};
408  }
409  std::vector<SUMaterialRef> material_refs(count, SU_INVALID);
410  res = SUModelGetMaterials(m_model, count, material_refs.data(), &count);
411  assert(res == SU_ERROR_NONE); _unused(res);
412  std::vector<Material> materials(count);
413  std::transform(material_refs.begin(), material_refs.end(), materials.begin(),
414  [](const SUMaterialRef& value){
415  return Material(value);
416  });
417  return materials;
418 }
419 
420 
421 void Model::add_materials(std::vector<Material>& materials) {
422  for (size_t i=0; i < materials.size(); i++) {
423  // Check that each material is not attached to another model
424  if (materials[i].attached()) {
425  throw std::invalid_argument("CW::Model::add_materials(): At least one of the Material objects passed is attached to another model. Use Material::copy() to create a new unattached Material object and try again.");
426  }
427  }
428  std::vector<SUMaterialRef> material_refs(materials.size(), SU_INVALID);
429  std::transform(materials.begin(), materials.end(), material_refs.begin(),
430  [](const Material& value){
431  return value.ref();
432  });
433  SUResult res = SUModelAddMaterials(m_model, materials.size(), material_refs.data());
434  assert(res == SU_ERROR_NONE); _unused(res);
435  for (size_t i=0; i < materials.size(); i++) {
436  materials[i].attached(true);
437  }
438 }
439 
440 
441 bool Model::material_exists(const Material& material) const {
442  std::vector<Material> materials = this->materials();
443  for (auto& mat : materials) {
444  if (mat == material) {
445  return true;
446  }
447  }
448  return false;
449 }
450 
451 
452 String Model::name() const {
453  if (!(*this)) {
454  throw std::logic_error("CW::Model::name(): Model is null");
455  }
456  SUStringRef name = SU_INVALID;
457  SUModelGetName(m_model, &name);
458  return String(name);
459 }
460 
461 
462 bool Model::name(const String& name_string) {
463  if (!(*this)) {
464  throw std::logic_error("CW::Model::name(): Model is null");
465  }
466  std::string std_string = name_string;
467  SUResult res = SUModelSetName(m_model, std_string.c_str());
468  if (res == SU_ERROR_NONE) {
469  return true;
470  }
471  return false;
472 }
473 
474 
475 size_t Model::num_faces() const {
476  if (!(*this)) {
477  throw std::logic_error("CW::Model::num_faces(): Model is null");
478  }
479  ModelStatistics model_statistics((*this));
480  return model_statistics.faces();
481 }
482 
483 OptionsManager Model::options()
484 {
485  SUOptionsManagerRef options_manager = SU_INVALID;
486  SUResult res = SUModelGetOptionsManager(m_model, &options_manager);
487  assert(res == SU_ERROR_NONE); _unused(res);
488  return OptionsManager(options_manager);
489 }
490 
491 /*
492 * Returns a key=>value list of options for the model.
493 * @see SUOptionsProviderRef
494 */
495 //std::vector<std::pair<std::string, std::string>> Model::options() const;
496 
497 /*
498 * Returns the path of the model.
499 */
500 // TODO - probably delete this, as there is no way to get the path of the model through the API.
501 // std::string Model::path() const {}
502 
503 /*
504 * Returns the first Entity object that a ray from a given point and direction vector will hit.
505 */
506 //Entity raytest(Point3D point, Vector3D vector);
507 
508 
509 SUResult Model::save(const std::string& file_path) {
510  if (!(*this)) {
511  throw std::logic_error("CW::Model::save(): Model is null");
512  }
513  const char * c_string = file_path.c_str();
514  SUResult res = SUModelSaveToFile(m_model, c_string);
515  return res;
516 }
517 
518 
519 bool Model::save_with_version(const std::string& file_path, SUModelVersion version) {
520  if (!(*this)) {
521  throw std::logic_error("CW::Model::save_with_version(): Model is null");
522  }
523  SUResult res = SUModelSaveToFileWithVersion(m_model, file_path.c_str(), version);
524  if (res == SU_ERROR_NONE) {
525  return true;
526  }
527  return false;
528 }
529 
530 
531 /*
532 * Returns the array of Scene objects attached to the model.
533 * @return scenes array of Scene objects.
534 */
535 // std::vector<Scene> scenes();
536 
537 
538 bool Model::set_attribute(AttributeDictionary& dict, const std::string& key, const TypedValue& value) {
539  if (!(*this)) {
540  throw std::logic_error("CW::Model::set_attribute(): Model is null");
541  }
542  return dict.set_attribute(key, value);
543 }
544 
545 bool Model::set_attribute(const std::string& dict_name, const std::string& key, const TypedValue& value) {
546  if (!(*this)) {
547  throw std::logic_error("CW::Model::set_attribute(): Model is null");
548  }
549  AttributeDictionary dict = attribute_dictionary(dict_name);
550  return set_attribute(dict, key, value);
551 }
552 
554 {
555  SURenderingOptionsRef ref = SU_INVALID;
556  SUResult res = SUModelGetRenderingOptions(m_model, &ref);
557  assert(res == SU_ERROR_NONE); _unused(res);
558  return RenderingOptions(ref);
559 }
560 
561 ShadowInfo Model::shadow_info()
562 {
563  SUShadowInfoRef ref = SU_INVALID;
564  SUResult res = SUModelGetShadowInfo(m_model, &ref);
565  assert(res == SU_ERROR_NONE); _unused(res);
566  return ShadowInfo(ref);
567 }
568 
569 
570 // set_datum()
571 
572 /*
573 * Returns the ShadowInfo object of the model.
574 */
575 // ShadowInfo shadow_info();
576 
577 /*
578 * Returns the list of styles in the model.
579 * @return styles vector array of Style objects
580 */
581 // std::vector<Style> styles();
582 
583 // tags
584 // tags=
585 
586 //std::string title() { return name();}
587 //std::string title(std::string name_value) { return name(name_value);}
588 
589 /******************
590 * ModelStatistics *
591 *******************/
592 ModelStatistics::ModelStatistics(SUModelStatistics model_statistics):
593  m_model_statistics(model_statistics)
594 {}
595 
596 ModelStatistics::ModelStatistics(const Model& model):
597  m_model_statistics(SUModelStatistics{})
598 {
599  SUResult res = SUModelGetStatistics(model, &m_model_statistics);
600  assert(res == SU_ERROR_NONE); _unused(res);
601 }
602 
603 int ModelStatistics::edges() const
604 {
605  return m_model_statistics.entity_counts[SUModelStatistics::SUEntityType_Edge];
606 }
607 
609 {
610  return m_model_statistics.entity_counts[SUModelStatistics::SUEntityType_Face];
611 }
612 int ModelStatistics::instances() const
613 {
614  return m_model_statistics.entity_counts[SUModelStatistics::SUEntityType_ComponentInstance];
615 }
616 int ModelStatistics::groups() const
617 {
618  return m_model_statistics.entity_counts[SUModelStatistics::SUEntityType_Group];
619 }
620 int ModelStatistics::definitions() const
621 {
622  return m_model_statistics.entity_counts[SUModelStatistics::SUEntityType_ComponentDefinition];
623 }
624 int ModelStatistics::layers() const
625 {
626  return m_model_statistics.entity_counts[SUModelStatistics::SUEntityType_Layer];
627 }
628 int ModelStatistics::materials() const
629 {
630  return m_model_statistics.entity_counts[SUModelStatistics::SUEntityType_Material];
631 }
632 int ModelStatistics::images() const
633 {
634  return m_model_statistics.entity_counts[SUModelStatistics::SUEntityType_Image];
635 }
636 
637 
638 
639 } /* namespace CW */
void add_layers(std::vector< Layer > &layers)
Definition: Model.cpp:362
std::string version_string() const
Definition: Model.cpp:118
bool layer_exists(const Layer &layer) const
Definition: Model.cpp:382
void add_materials(std::vector< Material > &materials)
Definition: Model.cpp:421
InstancePath instance_path(const String &persistent_id) const
Definition: Model.cpp:276
TypedValue get_attribute(const std::string &key, const TypedValue &default_value) const
bool operator!() const
Definition: Model.cpp:114
int faces() const
Definition: Model.cpp:608
RenderingOptions rendering_options()
RenderingOptions.
Definition: Model.cpp:553
Definition: Color.hpp:34
bool material_exists(const Material &material) const
Definition: Model.cpp:441
bool add_definition(ComponentDefinition &definition)
Definition: Model.cpp:147
SUModelRef ref() const
Definition: Model.cpp:91
bool set_attribute(const std::string &key, const TypedValue &value)