kenchan
November 24, 2020, 11:10am
1
I have a few questions regarding the SketchUp C SDK
I can’t find a SUTypedValueGetShort(…) or SUTypedValueSetShort(…) why?
Can I use another function, if so what should I use?
I can’t find a function to save a component to a file similar the the Save As… feature on the component context menu.
How can I do this with the SDK?
How can I change the Loaded From property of a component with the SDK?
Thanks in advance for any advice offered.
I would think you can use one of the integer types. There is:
SUTypedValueGetInt16
… and …
SUTypedValueGetInt32
… along with their setter functions.
Unfortunately, the component save feature in the C API is bugged and we filed an issue in the API Issue Tracker (see below.) What is really needed is a SUComponentDefinitionSaveAs
function that works the same as the Ruby API Sketchup::ComponentDefinition#save_as
method.
(If you are using the C SDK from within a “live” SketchUp application instance, then you may be able to call the Ruby method from C.)
opened 03:54PM - 04 Oct 19 UTC
closed 03:51PM - 08 Feb 22 UTC
bug
wontfix
C API
SketchUp
logged
The issue is that SUComponentInstanceGetTransform always returns identity when r… equesting on a parsed component instance from a [model skp file](https://forums.sketchup.com/uploads/short-url/aWMKUyX8PiMCa4IAIGC2CnaCjyn.skp).
Consider the following code:
```cpp
#include <stdio.h>
#include <iostream>
#include <SketchUpAPI/common.h>
#include <SketchUpAPI/geometry.h>
#include <SketchUpAPI/initialize.h>
#include <SketchUpAPI/model/model.h>
#include <SketchUpAPI/model/entities.h>
#include <SketchUpAPI/model/face.h>
#include <SketchUpAPI/model/edge.h>
#include <SketchUpAPI/model/vertex.h>
#include <SketchUpAPI/model/component_definition.h>
#include <SketchUpAPI/model/component_instance.h>
#include <SketchUpAPI/geometry/transformation.h>
int main(int n, char **args) {
// Always initialize the API before using it
SUInitialize();
if (n > 1 && !strcmp(1[args], "display"))
{
SUModelRef model = SU_INVALID;
SUModelCreateFromFile(&model, "new_model.skp");
SUEntitiesRef entities = SU_INVALID;
SUModelGetEntities(model, &entities);
size_t count = 0;
SUEntitiesGetNumInstances(entities, &count);
std::cout << "instance count: " << count << "\n";
SUComponentInstanceRef* instances = (SUComponentInstanceRef*)_alloca(count * sizeof * instances);
size_t szinstances;
SUEntitiesGetInstances(entities, count, instances, &szinstances);
std::cout << "returned instances count: " << szinstances << "\n";
while (szinstances--)
{
struct SUTransformation matrix;
SUComponentInstanceGetTransform(instances[szinstances], &matrix);
struct FloatingPosition {
double x, z, y, d;
} pos = { .x = matrix.values[12], .z = matrix.values[14], .y = matrix.values[13], .d = matrix.values[15] };
printf("%f, %f, %f\n", pos.x, pos.y, pos.z);
}
return 0;
}
// Create an empty model
SUModelRef model = SU_INVALID;
SUResult res = SUModelCreate(&model);
// It's best to always check the return code from each SU function call.
// Only showing this check once to keep this example short.
if (res != SU_ERROR_NONE)
return 1;
// Get the entity container of the model
SUEntitiesRef entities = SU_INVALID;
SUModelGetEntities(model, &entities);
// Create a loop input describing the vertex ordering for a face's outer loop
SULoopInputRef outer_loop = SU_INVALID;
SULoopInputCreate(&outer_loop);
for (size_t i = 0; i < 4; ++i) {
SULoopInputAddVertexIndex(outer_loop, i);
}
// Create the face
SUFaceRef face = SU_INVALID;
SUPoint3D vertices[4] = { { 0, 0, 0 },
{ 100, 100, 0 },
{ 100, 100, 100 },
{ 0, 0, 100 } };
SUFaceCreate(&face, vertices, &outer_loop);
// Add the face to the entities
SUComponentDefinitionRef def = SU_INVALID;
SUComponentDefinitionCreate(&def);
SUEntitiesRef defentities = SU_INVALID;
SUComponentDefinitionGetEntities(def, &defentities);
SUEntitiesAddFaces(defentities, 1, &face);
SUComponentInstanceRef instance = SU_INVALID;
SUComponentDefinitionCreateInstance(def, &instance);
static SUTransformation matrixtransform;
constexpr SUVector3D point = { 666, 666, 666 };
SUTransformationTranslation(&matrixtransform, &point);
SUComponentInstanceSetTransform(instance, &matrixtransform);
SUEntitiesAddInstance(entities, instance, nullptr);
//comment the next line to make it work
SUComponentInstanceSaveAs(instance, "instance.skp");
// Save the in-memory model to a file
res = SUModelSaveToFile(model, "new_model.skp");
// Must release the model or there will be memory leaks
SUModelRelease(&model);
// Always terminate the API when done using it
SUTerminate();
if (res != SU_ERROR_NONE) {
std::cout << "Unable to save document!";
return 1;
}
std::cout << "Document saved!";
return 0;
}
```
I'm using SDK_WIN_x64_2019-2-222. The above code will always print 0.000000, 0.000000, 0.000000 - component_instance_name - no matter if the component instance have any transformation taken in place (I exported them to skp with a transformation).
There is no current way in either the Ruby or C APIs to do this.
You will need to use SUModelLoadDefinition
to load the replacement definition, then get and save any properties you need to clone the instance (foremost the instance’s transform, but also perhaps it material and attribute dictionaries,) and then create a new instance at the same location using the properties you saved from the old instance.
You might also need to copy some of the old definition’s behaviors to the new definition.
Lastly, you would remove the old instance, and perhaps the old definition if it no longer has any instances in the model (or other component’s entities.)
Christina logged a feature request issue for both APIs …
opened 11:03AM - 13 Feb 18 UTC
closed 12:58PM - 28 Feb 23 UTC
bug
Ruby API
C API
SketchUp
logged
fixed-SU2023.0
The current DefinitionList.load method is a drag to work with. It refuses to loa… d components from paths associated withe existing components that haven't been modified (since when, I don't know). Instead it just returns the existing component, even if the external file has been modified.
Sometimes it fails to load a new component even if the existing component is modified, but I'm not sure when or why (when the existing component was modified within the same operation as load was called maybe?). For these cases we have to first save out the existing component to a temp file, just to dissociate it with the path. This hack causes other issues though as ComponentDefinition.save_as doesn't seem to record to the undo stack (despite changing the path property of the definition).
DefinitionList.load has so many weird behaviors and exceptions it's hard to even summarize it; I may have gotten this wrong. I literally get a headache from trying to figure out this mess. If someone could summarize the current behavior as pseudo code or something that would be appreciated.
To reload/relink a specific CompoenntDefiniton today you have to make sure Definitionlist.load regards it as a separate component to be able to load it at all, then you have to replace all references to the old component with the new one, make sure the name is correct (not having #1 appended), and finally purge the old definition.
I suggest a ComponentDefinition.reload method that by default reloads the definition from its associated path. The new data should be loaded into the existing ComponentDefinition object, with all instances and Ruby references now referring to the new data. This method should also take an optional path argument to relink the component from a custom location. This should be possible even with another component associated by that path and should not interfere with that other component in any way. The only way this method is affected by other definitions is that it makes sure the loaded component doesn't get a name that is already used by another component.
Simply but this should be a Ruby handle for what in the GUI is accessed from context menu > Reload...
````ruby
model = Sketchup.active_model
# Reload the first component from it's associated path, or raise error if internal (no associated path)
# or file is missing.
# The ComponentDefinition object is still the same, but its geometric content, name and other properties
# are loaded from the file.
model.definitions.first.reload
# Reload the first component from given path.
# If there is already a component in the model associated by this path, reload anyway and have
# two separate ComponentDefinitions both associated with the same file.
# If the component name inside the external file is already used by another component
# (regardless of whether it's associated by the same path) a new unique name is created for the
# loaded component, just as when a component is made unique.
model.definitions.first.reload(path: "C:/Users/Eneroth/SketchUp/Model.skp")
````
I also suggest an improved/corrected DefinitionList.load method that just doesn't do a any stupid stuff by its own. By default it should behave identically to pasting a component instance from the clipboard in the GUI, i.e. check if the GUID of the external file is used by an existing ComponentDefinition, if so return that, otherwise create a new CompoenntDefiniton and assure there are no name collisions.
There could be an optional argument to force loading into a separate ComponentDefiniton object even if there is an identical ComponentDefinition already present in the model (=identical GUID). This would be useful if the developer wants to edit the loaded component directly after loading, and don't want to affect any existing instances. This would of course create a new unique name, and therefore a unique GUID, directly on load.
````ruby
# Loading from the same path could return the same CompoenntDefinition,
# but ONLY if the GUID is identical.
model = Sketchup.active_model
model.definitions.size # => 0
model.definitons.load("C:/Users/Eneroth/SketchUp/Model.skp")
model.definitions.size # => 1
model.definitons.load("C:/Users/Eneroth/SketchUp/Model.skp")
model.definitions.size # => 1
# *Editing external file, or internal component here*
# Now a new ComponenDefinitiont is created.
model.definitons.load("C:/Users/Eneroth/SketchUp/Model.skp")
model.definitions.size # => 2
# The method can also be forced to load as a new unique ComponentDefinition,
# even with no modifications to the file or existing component(s).
model.definitons.load("C:/Users/Eneroth/SketchUp/Model.skp", force_separate: true)
model.definitions.size # => 3
# In this example all components are associated by the same path, but are still separate objects.
model.definitions.map(&:path) =>
[
"C:/Users/Eneroth/SketchUp/Model.skp",
"C:/Users/Eneroth/SketchUp/Model.skp",
"C:/Users/Eneroth/SketchUp/Model.skp"
]
````
Perhaps the new DefinitionList.load needs to have a different name, and the old be kept as it is but deprecated, to assure backward compatibility.
This has been discussed in a [forum thread](https://forums.sketchup.com/t/fix-definitionlist-load-problems-in-some-situations/5655) and ThomThom has already logged this as SU-30933 and SU-31274.
1 Like
kenchan
November 24, 2020, 11:52pm
3
Dan, thank you for your detailed reply. I look forward to the requested new features/bugs being fixed.
In the meantime I will try your suggestions and see if I can do a work around.
Thanks again.
1 Like