Bool AddForceCycleElement(Int32 id) { BaseObject* obj = BaseObject::Alloc(id); if (!obj) return false; AddChild(CMB_FORCE, id, obj->GetName() + "&i" + String::IntToString(id)); BaseObject::Free(obj); return true; }
Bool ExecuteAutoConnect() { ConnectOptions options; Bool addDynamicsTag = false, inheritDynamicsTag = true; GetInt32(CMB_FORCE, options.forcePluginId); GetInt32(CMB_TYPE, options.forceType); GetInt32(CMB_MODE, options.connectMode); GetInt32(EDT_MAXCONN, options.maxConnections); GetFloat(EDT_RADIUS, options.radius); GetBool(CHK_CLOSED, options.closedChain); GetBool(CHK_ADDDYNAMICS, addDynamicsTag); GetBool(CHK_COMPOUND, inheritDynamicsTag); // Create an InExcludeData for the selection object. GeData ge_selection(CUSTOMGUI_INEXCLUDE_LIST, DEFAULTVALUE); auto selectionList = static_cast<InExcludeData*>( ge_selection.GetCustomDataType(CUSTOMGUI_INEXCLUDE_LIST)); if (!selectionList) return false; // Get the active document and object. BaseDocument* doc = GetActiveDocument(); if (!doc) return false; BaseObject* op = doc->GetActiveObject(); if (!op) return false; // Create the root object that will contain the connectors. AutoFree<BaseObject> root(BaseObject::Alloc(Onull)); if (!root) return false; // Function to create a dynamics tag. auto fAddDynamicsTag = [doc] (BaseObject* op, Int32 mode) { // Create a dynamics tag for the root object if it // does not already exist. BaseTag* dyn = op->GetTag(ID_RIGIDBODY); if (!dyn) { dyn = op->MakeTag(ID_RIGIDBODY); if (dyn) doc->AddUndo(UNDOTYPE_NEW, dyn); } // Update the parameters. if (dyn) { dyn->SetParameter(RIGID_BODY_HIERARCHY, mode, DESCFLAGS_SET_0); doc->AddUndo(UNDOTYPE_CHANGE_SMALL, dyn); } }; // This list will contain all objects that should be connected. // While collecting, create the dynamics tags. maxon::BaseArray<BaseObject*> objects; doc->StartUndo(); for (BaseObject* child=op->GetDown(); child; child=child->GetNext()) { objects.Append(child); if (addDynamicsTag && inheritDynamicsTag) fAddDynamicsTag(child, RIGID_BODY_HIERARCHY_COMPOUND); } if (addDynamicsTag && !inheritDynamicsTag) fAddDynamicsTag(op, RIGID_BODY_HIERARCHY_INHERIT); // If no objects where collected, quit already. if (objects.GetCount() <= 0) { doc->EndUndo(); doc->DoUndo(false); return true; } // Create the connection objects. ConnectObjects(op->GetName() + ": ", objects, options); // Fill the selection list and insert the objects. for (auto it=options.output.Begin(); it != options.output.End(); ++it) { (*it)->InsertUnderLast(root); doc->AddUndo(UNDOTYPE_NEW, *it); selectionList->InsertObject(*it, 0); } root->SetName(op->GetName() + ": " + root->GetName() + " (" + options.forceName + ")"); doc->InsertObject(root, nullptr, nullptr); doc->AddUndo(UNDOTYPE_NEW, root); // Create the selection object. if (selectionList->GetObjectCount() > 0) { BaseObject* selection = BaseObject::Alloc(Oselection); if (selection) { selection->SetParameter(SELECTIONOBJECT_LIST, ge_selection, DESCFLAGS_SET_0); selection->SetName(op->GetName() + ": " + options.forceName + " (" + selection->GetName() + ")"); doc->InsertObject(selection, nullptr, nullptr); doc->AddUndo(UNDOTYPE_NEW, selection); } ActiveObjectManager_SetMode(ACTIVEOBJECTMODE_OBJECT, false); doc->SetActiveObject(selection); } else doc->SetActiveObject(root); root.Release(); doc->EndUndo(); EventAdd(); return true; }
Bool ConnectObjects( String prefix, const maxon::BaseArray<BaseObject*>& objects, ConnectOptions& options) { // Check if the object can successfully be allocatd for the // specified plugin id. BaseObject* test = BaseObject::Alloc(options.forcePluginId); if (!test) { GeDebugOut("Can not allocate plugin object " + String::IntToString(options.forcePluginId)); return false; } // Get the name of the object so we can use it later. options.forceName = test->GetName(); BaseObject::Free(test); if (!prefix.Content()) prefix = options.forceName + ": "; // Final result of the function. Bool success = true; // Create the ConnectionList for the objects. ConnectionList list; switch (options.connectMode) { case CMB_MODE_ALL: { const auto end = objects.End(); for (auto it=objects.Begin(); it != end; ++it) { for (auto jt=objects.Begin(); jt != end; ++jt) { if (*it == *jt) continue; if (list.HasConnection(*it, *jt)) continue; list.Append(Connection(*it, *jt)); } } break; } case CMB_MODE_NEIGHBOR: { const auto end = objects.End(); for (auto it=objects.Begin(); it != end; ++it) { // Find the nearest object. BaseObject* nearest = nullptr; Float delta; for (auto jt=objects.Begin(); jt != end; ++jt) { if (*it == *jt) continue; if (list.HasConnection(*it, *jt)) continue; Connection conn(*it, *jt); if (!nearest || conn.delta < delta) { nearest = *jt; delta = conn.delta; } } if (nearest) list.Append(Connection(*it, nearest)); } break; } case CMB_MODE_CHAIN: { options.radius = 0; options.maxConnections = 0; const auto first = objects.Begin(); for (auto it=objects.Begin(); it != objects.End(); ++it) { Bool isLast = (it + 1) == objects.End(); if (isLast && (options.closedChain && *it != *first)) { Connection conn(*it, *first); list.Append(conn); } else if (!isLast) { Connection conn(*it, *(it + 1)); list.Append(conn); } } break; } default: GeDebugOut(String(__FUNCTION__) + ": Invalid connectMode"); return false; } // Sort the list by distance. list.SortByDelta(); // This map contains the number of connections each // object already has to make sure no object exceeds the // maximum number if connections. maxon::Bool created = false; maxon::HashMap<C4DAtom*, Int32> map; // Iterate over all connections and establish them. const auto end = list.End(); for (auto it=list.Begin(); it != end; ++it) { if (options.radius > 0.000001 && it->delta > options.radius) continue; // Find and eventually initialize the entries in the map. auto entry1 = map.FindOrCreateEntry(it->obj1, created); if (created) entry1->GetValue() = 0; auto entry2 = map.FindOrCreateEntry(it->obj2, created); if (created) entry2->GetValue() = 0; // Determine if the maximum number of connections is reached. const Int32 nConn1 = entry1->GetValue(); const Int32 nConn2 = entry2->GetValue(); if (options.maxConnections > 0 && (nConn1 >= options.maxConnections || nConn2 >= options.maxConnections)) continue; // Create the force object. BaseObject* force = BaseObject::Alloc(options.forcePluginId); if (!force) { success = false; break; } // Update the parameters. force->SetParameter(FORCE_TYPE, options.forceType, DESCFLAGS_SET_0); force->SetParameter(FORCE_OBJECT_A, it->obj1, DESCFLAGS_SET_0); force->SetParameter(FORCE_OBJECT_B, it->obj2, DESCFLAGS_SET_0); force->SetBit(BIT_ACTIVE); force->SetName(prefix + it->obj1->GetName() + " - " + it->obj2->GetName()); ++entry1->GetValue(); ++entry2->GetValue(); options.output.Append(force); // Position the force object in between the two objects. Matrix mg; mg.off = (it->obj1->GetMg().off + it->obj2->GetMg().off) * 0.5; force->SetMl(mg); } return true; }