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; }