/* * receiver - points to the base room frustum, which portal leads to - it's taken from the portal! * returns a pointer to newly generated frustum. */ std::shared_ptr<Frustum> Frustum::portalFrustumIntersect(Portal *portal, std::shared_ptr<Frustum> emitter, Render *render) { assert(emitter); if(!portal->dest_room) return nullptr; if(portal->normal.distance(render->camera()->m_pos) < -SPLIT_EPSILON) // non face or degenerate to the line portal { return nullptr; } if(!portal->dest_room->frustum.empty() && emitter->hasParent(portal->dest_room->frustum.front())) { return nullptr; // Abort infinite loop! } bool in_dist = false, in_face = false; for(const btVector3& v : portal->vertices) { if(!in_dist && render->camera()->frustum->norm.distance(v) < render->camera()->m_distFar) in_dist = true; if(!in_face && emitter->norm.distance(v) > 0.0) in_face = true; if(in_dist && in_face) break; } if(!in_dist || !in_face) return nullptr; /* * Search for the first free room's frustum */ portal->dest_room->frustum.emplace_back(std::make_shared<Frustum>()); auto current_gen = portal->dest_room->frustum.back(); current_gen->splitPrepare(portal); // prepare for clipping if(current_gen->split_by_plane(emitter->norm)) // splitting by main frustum clip plane { for(size_t i = 0; i < emitter->vertices.size(); i++) { const auto& n = emitter->planes[i]; if(!current_gen->split_by_plane(n)) { portal->dest_room->frustum.pop_back(); return nullptr; } } current_gen->genClipPlanes(render->camera()); // all is OK, let's generate clip planes current_gen->parent = emitter; // add parent pointer current_gen->parents_count = emitter->parents_count + 1; if(portal->dest_room->max_path < current_gen->parents_count) { portal->dest_room->max_path = current_gen->parents_count; // maximum path to the room } return current_gen; } portal->dest_room->frustum.pop_back(); return nullptr; }
/** @brief Set keys in an atomic and universal way. * * @pre kdbGet() must be called before kdbSet(): * - initially (after kdbOpen()) * - after conflict errors in kdbSet(). * * @pre The @p returned KeySet must be a valid KeySet, e.g. constructed * with ksNew(). * * @pre The @p parentKey Key must be a valid Key, e.g. constructed with * keyNew(). * * If you pass NULL on any parameter kdbSet() will fail immediately without doing anything. * * With @p parentKey you can give an hint which part of the given keyset * is of interest for you. Then you promise to only modify or * remove keys below this key. All others would be passed back * as they were retrieved by kdbGet(). * * @par Errors * If some error occurs: * - kdbSet() will leave the KeySet's * internal cursor on the key that generated the error. * - Error information will be written into the metadata of * the parent key. * - None of the keys are actually committed in this situation, i.e. no * configuration file will be modified. * * In case of errors you should present the error message to the user and let the user decide what * to do. Possible solutions are: * - remove the problematic key and use kdbSet() again (for validation or type errors) * - change the value of the problematic key and use kdbSet() again (for validation errors) * - do a kdbGet() (for conflicts, i.e. error 30) and then * - set the same keyset again (in favour of what was set by this user) * - drop the old keyset (in favour of what was set from another application) * - merge the original, your own and the other keyset * - export the configuration into a file (for unresolvable errors) * - repeat the same kdbSet might be of limited use if the user does * not explicitly request it, because temporary * errors are rare and its unlikely that they fix themselves * (e.g. disc full, permission problems) * * @par Optimization * Each key is checked with keyNeedSync() before being actually committed. * If no key of a backend needs to be synced * any affairs to backends are omitted and 0 is returned. * * @snippet kdbset.c set * * showElektraErrorDialog() and doElektraMerge() need to be implemented * by the user of Elektra. For doElektraMerge a 3-way merge algorithm exists in * libelektra-tools. * * @param handle contains internal information of @link kdbOpen() opened @endlink key database * @param ks a KeySet which should contain changed keys, otherwise nothing is done * @param parentKey is used to add warnings and set an error * information. Additionally, its name is an hint which keys * should be committed (it is possible that more are changed). * - cascading keys (starting with /) will set the path in all namespaces * - / will commit all keys * - metanames will be rejected (error 104) * - empty/invalid (error 105) * @retval 1 on success * @retval 0 if nothing had to be done, no changes in KDB * @retval -1 on failure, no changes in KDB * @see keyNeedSync() * @see ksCurrent() contains the error key * @see kdbOpen() and kdbGet() that must be called first * @see kdbClose() that must be called afterwards * @ingroup kdb */ int kdbSet (KDB * handle, KeySet * ks, Key * parentKey) { elektraNamespace ns = keyGetNamespace (parentKey); if (ns == KEY_NS_NONE) { return -1; } Key * oldError = keyNew (keyName (parentKey), KEY_END); copyError (oldError, parentKey); if (ns == KEY_NS_META) { clearError (parentKey); // clear previous error to set new one ELEKTRA_SET_ERRORF (104, parentKey, "metakey with name \"%s\" passed to kdbSet", keyName (parentKey)); keyDel (oldError); return -1; } if (ns == KEY_NS_EMPTY) { ELEKTRA_ADD_WARNING (105, parentKey, "invalid key name passed to kdbSet"); } if (!handle || !ks) { clearError (parentKey); // clear previous error to set new one ELEKTRA_SET_ERROR (37, parentKey, "handle or ks null pointer"); keyDel (oldError); return -1; } int errnosave = errno; Key * initialParent = keyDup (parentKey); ELEKTRA_LOG ("now in new kdbSet (%s) %p %zd", keyName (parentKey), (void *)handle, ksGetSize (ks)); elektraGlobalSet (handle, ks, parentKey, PRESETSTORAGE, INIT); elektraGlobalSet (handle, ks, parentKey, PRESETSTORAGE, MAXONCE); elektraGlobalSet (handle, ks, parentKey, PRESETSTORAGE, DEINIT); ELEKTRA_LOG ("after presetstorage maxonce(%s) %p %zd", keyName (parentKey), (void *)handle, ksGetSize (ks)); Split * split = splitNew (); Key * errorKey = 0; if (splitBuildup (split, handle, parentKey) == -1) { clearError (parentKey); // clear previous error to set new one ELEKTRA_SET_ERROR (38, parentKey, "error in splitBuildup"); goto error; } // 1.) Search for syncbits int syncstate = splitDivide (split, handle, ks); if (syncstate == -1) { clearError (parentKey); // clear previous error to set new one ELEKTRA_SET_ERROR (8, parentKey, keyName (ksCurrent (ks))); goto error; } ELEKTRA_ASSERT (syncstate == 0 || syncstate == 1, "syncstate not 0 or 1, but %d", syncstate); // 2.) Search for changed sizes syncstate |= splitSync (split); ELEKTRA_ASSERT (syncstate <= 1, "syncstate not equal or below 1, but %d", syncstate); if (syncstate != 1) { /* No update is needed */ keySetName (parentKey, keyName (initialParent)); if (syncstate < 0) clearError (parentKey); // clear previous error to set new one if (syncstate == -1) { ELEKTRA_SET_ERROR (107, parentKey, "Assert failed: invalid namespace"); } else if (syncstate < -1) { ELEKTRA_SET_ERROR (107, parentKey, keyName (split->parents[-syncstate - 2])); } keyDel (initialParent); splitDel (split); errno = errnosave; keyDel (oldError); return syncstate == 0 ? 0 : -1; } ELEKTRA_ASSERT (syncstate == 1, "syncstate not 1, but %d", syncstate); splitPrepare (split); clearError (parentKey); // clear previous error to set new one if (elektraSetPrepare (split, parentKey, &errorKey, handle->globalPlugins) == -1) { goto error; } else { // no error, restore old error copyError (parentKey, oldError); } keySetName (parentKey, keyName (initialParent)); elektraGlobalSet (handle, ks, parentKey, PRECOMMIT, INIT); elektraGlobalSet (handle, ks, parentKey, PRECOMMIT, MAXONCE); elektraGlobalSet (handle, ks, parentKey, PRECOMMIT, DEINIT); elektraSetCommit (split, parentKey); elektraGlobalSet (handle, ks, parentKey, COMMIT, INIT); elektraGlobalSet (handle, ks, parentKey, COMMIT, MAXONCE); elektraGlobalSet (handle, ks, parentKey, COMMIT, DEINIT); splitUpdateSize (split); keySetName (parentKey, keyName (initialParent)); elektraGlobalSet (handle, ks, parentKey, POSTCOMMIT, INIT); elektraGlobalSet (handle, ks, parentKey, POSTCOMMIT, MAXONCE); elektraGlobalSet (handle, ks, parentKey, POSTCOMMIT, DEINIT); for (size_t i = 0; i < ks->size; ++i) { // remove all flags from all keys clear_bit (ks->array[i]->flags, KEY_FLAG_SYNC); } keySetName (parentKey, keyName (initialParent)); keyDel (initialParent); splitDel (split); keyDel (oldError); errno = errnosave; return 1; error: keySetName (parentKey, keyName (initialParent)); elektraGlobalError (handle, ks, parentKey, PREROLLBACK, INIT); elektraGlobalError (handle, ks, parentKey, PREROLLBACK, MAXONCE); elektraGlobalError (handle, ks, parentKey, PREROLLBACK, DEINIT); elektraSetRollback (split, parentKey); if (errorKey) { Key * found = ksLookup (ks, errorKey, 0); if (!found) { ELEKTRA_ADD_WARNING (82, parentKey, keyName (errorKey)); } } keySetName (parentKey, keyName (initialParent)); elektraGlobalError (handle, ks, parentKey, POSTROLLBACK, INIT); elektraGlobalError (handle, ks, parentKey, POSTROLLBACK, MAXONCE); elektraGlobalError (handle, ks, parentKey, POSTROLLBACK, DEINIT); keySetName (parentKey, keyName (initialParent)); keyDel (initialParent); splitDel (split); errno = errnosave; keyDel (oldError); return -1; }