[286] | 1 | // This file is a part of Framsticks SDK. http://www.framsticks.com/ |
---|
[1111] | 2 | // Copyright (C) 1999-2021 Maciej Komosinski and Szymon Ulatowski. |
---|
[286] | 3 | // See LICENSE.txt for details. |
---|
[271] | 4 | |
---|
[235] | 5 | #include "modelgeoclass.h" |
---|
| 6 | #include "modelgeometryinfo.h" |
---|
| 7 | #include <frams/vm/classes/collectionobj.h> |
---|
| 8 | #include <frams/vm/classes/3dobject.h> |
---|
| 9 | |
---|
| 10 | #define FIELDSTRUCT ModelGeometry |
---|
[242] | 11 | static ParamEntry modelgeo_paramtab[] = |
---|
[235] | 12 | { |
---|
[1111] | 13 | { "Creature: Geometry", 1, 6, "ModelGeometry", |
---|
[548] | 14 | "Approximately estimates sizes, volume, and area of a Model based on the geometry of its parts.\n" |
---|
[242] | 15 | "Example usage:\n" |
---|
| 16 | "Simulator.print(ModelGeometry.forModel(Model.newFromString(\"//0\\np:sh=1\\n\")).area());\n\n" |
---|
| 17 | "ModelGeometry.geom_density refers to the global simulator parameter (also available in GUI).\n" |
---|
| 18 | "To set geom_density for individual ModelGeometry objects:\n" |
---|
[548] | 19 | "var mg=ModelGeometry.forModel(GenePools[0][0].getModel()); mg.geom_density=2; GenePools[0][0].data->area=mg.area();\n" }, |
---|
[1158] | 20 | { "geom_density", 0, 0, "Density", "f 0.01 100.0 3.0", FIELD(density), "The number of samples (per unit length in one dimension) that affects the precision of estimation of geometrical properties." }, //Note #1: we used 'geom_density' instead of 'density' to make the name more unique - because sim_params merges all configuration fields in a single namespace. Note #2: "density" needs better, more interpretable and more reliable parametrization for surface sampling... |
---|
[242] | 21 | { "forModel", 0, PARAM_USERHIDDEN, "", "p oModelGeometry(oModel)", PROCEDURE(p_formodel), "The returned ModelGeometry object can be used to calculate geometric properties (volume, area, sizes) of the associated model. The density is copied from the current global ModelGeometry.geom_density on object creation." }, |
---|
| 22 | { "volume", 0, PARAM_NOSTATIC | PARAM_USERHIDDEN, "volume", "p f()", PROCEDURE(p_volume), }, |
---|
| 23 | { "area", 0, PARAM_NOSTATIC | PARAM_USERHIDDEN, "area", "p f()", PROCEDURE(p_area), }, |
---|
[1115] | 24 | { "voxels", 0, PARAM_NOSTATIC | PARAM_USERHIDDEN, "voxels", "p oVector()", PROCEDURE(p_voxels), "Returns a Vector of Pt3D objects from a regular 3D grid (sampled according to ModelGeometry.geom_density) that are inside of the Model body (Parts and Joints)."}, |
---|
[242] | 25 | { "sizesAndAxes", 0, PARAM_NOSTATIC | PARAM_USERHIDDEN, "sizesAndAxes", "p oVector()", PROCEDURE(p_sizesandaxes), "The returned vector contains XYZ (sizes) and Orient (axes) objects." }, |
---|
| 26 | { 0, 0, 0, }, |
---|
[235] | 27 | }; |
---|
| 28 | #undef FIELDSTRUCT |
---|
| 29 | |
---|
| 30 | ExtObject ModelGeometry::makeDynamicObject(ModelGeometry* mg) |
---|
[242] | 31 | { |
---|
| 32 | return ExtObject(&mg->par, mg); |
---|
| 33 | } |
---|
[235] | 34 | |
---|
| 35 | ModelGeometry::ModelGeometry(ModelObj *mo) |
---|
[1111] | 36 | :par(modelgeo_paramtab, this) |
---|
[235] | 37 | { |
---|
[242] | 38 | cached_for_density = -1; //invalid value, will be updated on first request |
---|
| 39 | invalidateAllCached(); |
---|
| 40 | model = mo; |
---|
| 41 | if (model != NULL) |
---|
| 42 | model->incref(); |
---|
[235] | 43 | } |
---|
| 44 | |
---|
| 45 | ModelGeometry::~ModelGeometry() |
---|
| 46 | { |
---|
[242] | 47 | if (model != NULL) |
---|
| 48 | model->decref(); |
---|
[235] | 49 | } |
---|
| 50 | |
---|
[242] | 51 | // Mark all 3 results as invalid. |
---|
| 52 | // Validity of these 3 values must be maintained independently, |
---|
| 53 | // as each of them is calculated by an individual call. |
---|
| 54 | void ModelGeometry::invalidateAllCached() |
---|
[235] | 55 | { |
---|
[242] | 56 | cached_volume = -1; |
---|
| 57 | cached_area = -1; |
---|
| 58 | cached_sizes.x = -1; |
---|
[1111] | 59 | cached_voxels.setEmpty(); |
---|
[242] | 60 | } |
---|
| 61 | |
---|
| 62 | // Invalidates cached results if a new density is requested |
---|
| 63 | // (called in all geometry calculation functions) |
---|
| 64 | void ModelGeometry::onDensityChanged() |
---|
| 65 | { |
---|
| 66 | if (cached_for_density != density) |
---|
[235] | 67 | { |
---|
[242] | 68 | invalidateAllCached(); |
---|
| 69 | cached_for_density = density; |
---|
[235] | 70 | } |
---|
| 71 | } |
---|
| 72 | |
---|
[242] | 73 | void ModelGeometry::p_formodel(ExtValue *args, ExtValue *ret) |
---|
[235] | 74 | { |
---|
[242] | 75 | Model *m = ModelObj::fromObject(*args); |
---|
| 76 | if (m != NULL) |
---|
| 77 | { |
---|
[1111] | 78 | if (m->getShapeType() == Model::SHAPETYPE_BALL_AND_STICK) |
---|
| 79 | { |
---|
[661] | 80 | Model *converted = new Model; |
---|
| 81 | converted->open(); |
---|
[1115] | 82 | converted->buildUsingSolidShapeTypes(*m, Part::SHAPE_CYLINDER); |
---|
[661] | 83 | converted->close(); |
---|
[1111] | 84 | m = converted; |
---|
| 85 | } |
---|
[242] | 86 | ModelGeometry *mg = new ModelGeometry((ModelObj*)m); |
---|
| 87 | mg->density = density; |
---|
| 88 | ret->setObject(ModelGeometry::makeDynamicObject(mg)); |
---|
| 89 | } |
---|
| 90 | else |
---|
| 91 | ret->setEmpty(); |
---|
[235] | 92 | } |
---|
| 93 | |
---|
[242] | 94 | void ModelGeometry::p_volume(ExtValue *args, ExtValue *ret) |
---|
[235] | 95 | { |
---|
[242] | 96 | onDensityChanged(); |
---|
| 97 | if (cached_volume < 0) //calculate if invalid |
---|
| 98 | cached_volume = ModelGeometryInfo::volume(*model, density); |
---|
| 99 | ret->setDouble(cached_volume); |
---|
[235] | 100 | } |
---|
| 101 | |
---|
[242] | 102 | void ModelGeometry::p_area(ExtValue *args, ExtValue *ret) |
---|
[235] | 103 | { |
---|
[242] | 104 | onDensityChanged(); |
---|
| 105 | if (cached_area < 0) //calculate if invalid |
---|
| 106 | cached_area = ModelGeometryInfo::area(*model, density); |
---|
| 107 | ret->setDouble(cached_area); |
---|
[235] | 108 | } |
---|
[242] | 109 | |
---|
[1111] | 110 | void ModelGeometry::p_voxels(ExtValue *args, ExtValue *ret) |
---|
| 111 | { |
---|
| 112 | onDensityChanged(); |
---|
| 113 | if (cached_voxels.isEmpty()) //calculate if invalid |
---|
| 114 | cached_voxels = ModelGeometryInfo::getVoxels(*model, density); |
---|
| 115 | ret->setObject(cached_voxels); |
---|
| 116 | } |
---|
| 117 | |
---|
[242] | 118 | void ModelGeometry::p_sizesandaxes(ExtValue *args, ExtValue *ret) |
---|
| 119 | { |
---|
| 120 | onDensityChanged(); |
---|
| 121 | if (cached_sizes.x < 0) //calculate if invalid |
---|
[658] | 122 | ModelGeometryInfo::findSizesAndAxes(*model, density, cached_sizes, cached_axes); |
---|
[242] | 123 | |
---|
| 124 | VectorObject* n = new VectorObject; |
---|
| 125 | n->data += new ExtValue(Pt3D_Ext::makeDynamicObject(cached_sizes)); |
---|
| 126 | n->data += new ExtValue(Orient_Ext::makeDynamicObject(new Orient_Ext(cached_axes))); |
---|
| 127 | ret->setObject(n->makeObject()); |
---|
| 128 | } |
---|