Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login
    1. Maxon Developers Forum
    2. intenditore
    3. Topics
    • Profile
    • Following 0
    • Followers 0
    • Topics 9
    • Posts 34
    • Best 2
    • Controversial 0
    • Groups 0

    Topics created by intenditore

    • intenditoreI

      pyDeformer mode: ID_MG_BASEEFFECTOR_POSITION works, but ID_MG_BASEEFFECTOR_SCALE doesn't

      Cinema 4D SDK
      • python r19 r20 • • intenditore
      6
      0
      Votes
      6
      Posts
      1.3k
      Views

      ferdinandF

      Hi,

      I am sorry, I hate it myself when people talk in acronyms, assuming everyone knows what they are referring to. PNG stands for Pseudo-random Number Generator. Here is an example for a simple trigonometric pseudo random hash function.

      Cheers,
      zipit

      """A simple example for a very simple "one-at-a-time" Pseudo-random Number Generator (PNG). It is basically just one line of code, which you can find on line 32. """ import c4d import math def hash_11(x, seed=1234, magic=(1234.4567, 98765.4321)): """Returns a pseudo random floating point hash for a floating point number. The hash will lie int the interval [-1, 1] and the function is a very simple generator that exploits the periodicity of the trigonometric functions. A more sophisticated approach would be to exploit avalanche behavior in bit-shift operations on binary data, like the Jenkins Rotation does for example. The postfix in the name (_11) is a common way to denote the inputs and outputs for PNGs. 11 means that it will take one (real) number and return one (real) number. 13 means that it takes one and returns three, i.e. returns an (euclidean) vector. Args: x (float): The value to hash into a pseudo random number. seed (int, optional): The seed value. This could also be a float. magic (tuple, optional): The magic numbers. The second one should be bigger then the first and both should be real numbers. Returns: float: The pseudo random hash for x in the interval [-1, 1]. """ return math.modf(math.sin(x + seed + magic[0]) * magic[1])[0] def hash_13(x, seed=1234, magic=(1234.4567, 98765.4321)): """Returns a pseudo random vector hash for a floating point number. Wraps around hash_11. Returns: c4d.Vector: The pseudo random hash for x in the interval [-1, 1]. """ vx = hash_11(x, seed, magic) vy = hash_11(x + vx, seed, magic) vz = hash_11(x + vy, seed, magic) return c4d.Vector(vx, vy, vz) def main(): """Entry point. """ # Some very crude statistics for the hashes. samples = int(1E6) # Generate 1E6 of each numbers = {i: hash_11(i) for i in range(samples)} vectors = {i: hash_13(i) for i in range(samples)} # Compute their arithmetic mean. amean_numbers = sum(numbers.values()) * (1./samples) amean_vectors = sum(vectors.values()) * (1./samples) # Report the results. print "First three random numbers: ", numbers.values()[:3] print "First three random vectors: ", vectors.values()[:3] msg = "Arithmetic mean of all random numbers (should converge to zero): " print msg, amean_numbers msg = "Arithmetic mean of all random vectors (should converge to zero): " print msg, amean_vectors if __name__ == "__main__": main() First three random numbers: [-0.8036933662078809, 0.20401213006516628, 0.6249060598929645] First three random vectors: [Vector(-0.804, -0.022, -0.872), Vector(0.204, 0.541, 0.115), Vector(0.625, 0.782, 0.896)] Arithmetic mean of all random numbers (should converge to zero): -0.000127638074863 Arithmetic mean of all random vectors (should converge to zero): Vector(0, 0, 0)
    • intenditoreI

      Get all neighboring points (including not connected by edge). Fast.

      Cinema 4D SDK
      • python • • intenditore
      5
      0
      Votes
      5
      Posts
      1.1k
      Views

      ManuelM

      hi,

      from time to time i'm saying thanks to @zipit , but for sure he's contributing a lot to the community.
      there's not too much to say more that what have been said.

      Brut force is the only option without using external python librairies. You can always use an external library if you want to use a kd-tree structure to be faster.

      Using cpp would be a better solution if you really want to do it fast. We have in our API a kd-tree as show in this kd-stree manual.

      You can see on github a full example on how to use it

      And here are some code of me playing with different way of sampling a kd-tree using parallel method and also using voxelization to get the closest polygon.

      //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- /// GetClosestPolygon using voxelization //---------------------------------------------------------------------------------------- static maxon::Result<void> GETCLOSEST_POLY(BaseDocument* doc) { iferr_scope; if (doc == nullptr) return maxon::IllegalArgumentError(MAXON_SOURCE_LOCATION); // ------- Create a mesh with one poly const maxon::Int32 polyCnt{ 500 }; PolygonObject *mesh = PolygonObject::Alloc(polyCnt * 4, polyCnt ); if (mesh == nullptr) return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION); maxon::LinearCongruentialRandom<maxon::Float32> random; CPolygon *polyAdrW = mesh->GetPolygonW(); Vector* padrW = mesh->GetPointW(); const Float polySize{ 10 }; for (maxon::Int i = 0; i < polyCnt; i++) { CPolygon poly; maxon::Vector pointA(i * polySize, 0, 0); maxon::Vector pointB(i * polySize, 0, polySize); maxon::Vector pointC(i * polySize + polySize, 0, polySize); maxon::Vector pointD(i * polySize + polySize, 0, 0); const maxon::Int pointIndice = i * 4; padrW[pointIndice] = pointA; padrW[pointIndice + 1] = pointB; padrW[pointIndice + 2] = pointC; padrW[pointIndice + 3] = pointD; poly.a = pointIndice; poly.b = pointIndice + 1; poly.c = pointIndice + 2; poly.d = pointIndice + 3; polyAdrW[i] = poly; } doc->InsertObject(mesh, nullptr, nullptr); maxon::DistanceQueryRef distanceQueryRef = maxon::DistanceCalculator().Create() iferr_return; distanceQueryRef.Init(mesh, true) iferr_return; maxon::PrimitiveInformation result; const maxon::Int samplePointCnt = polyCnt; //---------------------------------------------------------------------------------------- /// sample using standard loop maxon::TimeValue start = maxon::TimeValue(maxon::TimeValue::NOW); for (maxon::Int i = 0; i < samplePointCnt; ++i) { const maxon::Vector pos{ i * polySize + polySize * 0.5, 0, 0 }; distanceQueryRef.GetClosestMeshPrimitive(pos, result); if (result.type == maxon::PRIMITIVETYPE::POLYGON) ApplicationOutput("pos @, polyIndex @ ", pos, result.GetRealPolyIndex()); else ApplicationOutput("not a polygon"); } ApplicationOutput("time to sample the points @ with MP", start.Stop()); //---------------------------------------------------------------------------------------- /// sample using parallelFor start = maxon::TimeValue(maxon::TimeValue::NOW); maxon::BaseArray< maxon::PrimitiveInformation> resultArray; resultArray.Resize(samplePointCnt) iferr_return; maxon::ParallelFor::Dynamic(0, samplePointCnt, [&resultArray, &polySize, &distanceQueryRef](maxon::Int i) { maxon::PrimitiveInformation result; const maxon::Vector pos{ i * polySize + polySize * 0.5, 0, 0 }; distanceQueryRef.GetClosestMeshPrimitive(pos, result); if (result.type == maxon::PRIMITIVETYPE::POLYGON) ApplicationOutput("pos @, polyIndex @ ", pos, result.GetRealPolyIndex()); else ApplicationOutput("not a polygon"); resultArray[i] = result; } ); ApplicationOutput("time to sample the points @ with MP", start.Stop()); for (auto& value : resultArray) { if (value.type == maxon::PRIMITIVETYPE::POLYGON) ApplicationOutput("polyIndex @ ", value.GetRealPolyIndex()); else ApplicationOutput("not a polygon"); } return maxon::OK; } //---------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------- /// Sample a KD tree with multi thread //---------------------------------------------------------------------------------------- class SampleTreeJob : public maxon::JobInterfaceTemplate<SampleTreeJob, maxon::Int> { public: SampleTreeJob() { }; MAXON_IMPLICIT SampleTreeJob(maxon::KDTree* kdtree, const maxon::Vector& samplePoint) { _kdtree = kdtree; _samplePoint = samplePoint; } // function operator with the actual workload maxon::Result<void> operator ()() { maxon::Int nearestIndex = _kdtree->FindNearest(maxon::JobRef::GetCurrentWorkerThreadIndex(), _samplePoint, nullptr); // store result return SetResult(std::move(nearestIndex)); } private: maxon::KDTree* _kdtree; maxon::Vector _samplePoint; }; static maxon::Result <maxon::BaseArray<maxon::Vector>> CreateArrayOfPoints() { iferr_scope; const maxon::Int pointCnt = 1000000; maxon::BaseArray<maxon::Vector> points; points.EnsureCapacity(pointCnt) iferr_return; maxon::LinearCongruentialRandom<maxon::Float32> random; for (maxon::Int i = 0; i < pointCnt; ++i) { maxon::Vector pos(maxon::DONT_INITIALIZE); pos.x = random.Get01() * 100000.0; pos.y = random.Get01() * 100000.0; pos.z = random.Get01() * 100000.0; points.Append(pos) iferr_return; } return points; } static maxon::Result<maxon::BaseArray<maxon::Vector>> CreateSamplePoints() { iferr_scope; // sample points const maxon::Int samplePointCnt = 10000; maxon::BaseArray<maxon::Vector> samplePoints; samplePoints.EnsureCapacity(samplePointCnt) iferr_return; for (maxon::Int i = 0; i < samplePointCnt; ++i) { const maxon::Vector pos{ i* 10.0 }; samplePoints.Append(pos) iferr_return; } return samplePoints; } static maxon::Result<void> KDTREE_MP(BaseDocument* doc) { iferr_scope; maxon::BaseArray<maxon::Vector> points = CreateArrayOfPoints() iferr_return; maxon::BaseArray<maxon::Vector> samplePoints = CreateSamplePoints() iferr_return; const maxon::Int samplePointCnt = samplePoints.GetCount(); const maxon::Int pointCnt = points.GetCount(); maxon::KDTree tree; tree.Init(maxon::JobRef::GetCurrentThreadCount()) iferr_return; // insert points for (maxon::Int i = 0; i < pointCnt; ++i) { tree.Insert(points[i], i) iferr_return; } // balance tree tree.Balance(); //---------------------------------------------------------------------------------------- /// sample using classic loop maxon::TimeValue start{ maxon::TimeValue::NOW }; for (const maxon::Vector& samplePoint : samplePoints) { // find nearest point const maxon::Int nearestIndex = tree.FindNearest(0, samplePoint, nullptr); } ApplicationOutput("time to sample the points @ ", start.Stop()); //---------------------------------------------------------------------------------------- /// sample using parallelFor start = maxon::TimeValue(maxon::TimeValue::NOW); maxon::ParallelFor::Dynamic(0, samplePointCnt, [&samplePoints, &tree](maxon::Int i) { const maxon::Vector samplePoint = samplePoints[i]; const maxon::Int nearestIndex = tree.FindNearest(maxon::JobRef::GetCurrentWorkerThreadIndex(), samplePoint, nullptr); } ); ApplicationOutput("time to sample the points @ with MP", start.Stop()); //---------------------------------------------------------------------------------------- /// Sample using job // create group start = maxon::TimeValue(maxon::TimeValue::NOW); maxon::JobGroupRef group = maxon::JobGroupRef::Create() iferr_return; for (auto i = 0 ; i < samplePointCnt; i++) { // create and add job const maxon::Vector samplePoint = samplePoints[i]; auto job = SampleTreeJob::Create(&tree, samplePoint) iferr_return; group.Add(job) iferr_return; } // enqueue jobs and wait group.Enqueue(); group.Wait(); ApplicationOutput("time to sample the points @ with jobs", start.Stop()); //---------------------------------------------------------------------------------------- /// sample using classic loop again start = maxon::TimeValue(maxon::TimeValue::NOW); for (const maxon::Vector& samplePoint : samplePoints) { // find nearest point const maxon::Int nearestIndex = tree.FindNearest(0, samplePoint, nullptr); } ApplicationOutput("time to sample the points @ second time to see if caches are envolved", start.Stop()); return maxon::OK; }

      Cheers,
      Manuel

    • intenditoreI

      TagData plugin to "return" Vertex map?

      Cinema 4D SDK
      • • • intenditore
      9
      0
      Votes
      9
      Posts
      1.5k
      Views

      intenditoreI

      Yeah, sure)

    • intenditoreI

      UI, Resource Descripion and all those .h, .str, .res

      Cinema 4D SDK
      • • • intenditore
      11
      0
      Votes
      11
      Posts
      1.9k
      Views

      intenditoreI

      Brah... Third time spent fighting with stupid typos! :
      @PluginStudent @zipit @r_gigante many thanks! I didn't assume you are using the same system inside the program and had no thought to take a look there!
      Seems adding the "," helped it.
      And, as I see, you must enumerate all the elements in your .h file, but though you can leave id blank.
      Thank you!
      %\

    • intenditoreI

      Undo for a TagData?

      Cinema 4D SDK
      • • • intenditore
      5
      0
      Votes
      5
      Posts
      803
      Views

      M

      As said previously this is not possible in the execute method to add an undo event because this execute method is called during the execute.

      But if I understood correctly you have an UserData attached then this UD drive children parameters. If that's the case you can react to this parameter change (coming from the GUI which is a Main Thread thing) and then apply the undo.
      You can find an example in https://developers.maxon.net/forum/topic/12479/update-button/3

      Cheers,
      Maxime.

    • intenditoreI

      Python tag - IsDirty() not working?

      General Talk
      • • • intenditore
      11
      0
      Votes
      11
      Posts
      2.2k
      Views

      intenditoreI

      @m_adam thank you, that's really confusing..

    • intenditoreI

      c4d.ObjectData plugin code execution sequence

      Cinema 4D SDK
      • python • • intenditore
      8
      0
      Votes
      8
      Posts
      1.0k
      Views

      ManuelM

      hi,

      without further feedback i'll set this thread as solved tomorrow.

      Cheer,
      Manuel

    • intenditoreI

      "History" xpresso node python analogue?

      Cinema 4D SDK
      • • • intenditore
      12
      0
      Votes
      12
      Posts
      1.5k
      Views

      ManuelM

      hi,

      can we considered this thread as solved ? (at least we answered your question)
      Without further feedback i'll set it to solved tomorrow.

      Cheers,
      Manuel

    • intenditoreI

      Registering controller's events from within C4D

      General Talk
      • • • intenditore
      4
      0
      Votes
      4
      Posts
      898
      Views

      intenditoreI

      Sorry for late reply, there was an issue with my account.
      Many thanks for comprehensive answers. The topic looks kind of mind bending now, not much easier than after I realised Api doesn't really have what I expected to have for the task accomplishment 😓
      Now I think the better way might be to simulate the mouse over the viewport, so it will be an independent app which will only need from the Cinema to know where the viewport is. That seems easier I hope.
      When my research or development will reach some success I will share what I learned here, maybe it will be handy for somebody else.
      Thanks once more!