Add part path smooth interpolation

Part path set default as Centripetal Catmull-Rom spline interpolated
master
huxingyi 2020-10-20 20:48:08 +09:30
parent 17e97785b3
commit 8a79fa06de
16 changed files with 543 additions and 109 deletions

View File

@ -541,6 +541,9 @@ HEADERS += src/partpreviewimagesgenerator.h
SOURCES += src/remeshhole.cpp
HEADERS += src/remeshhole.h
SOURCES += src/centripetalcatmullromspline.cpp
HEADERS += src/centripetalcatmullromspline.h
SOURCES += src/main.cpp
HEADERS += src/version.h

View File

@ -1,120 +1,120 @@
DUST3D 1.0 xml 0000000193
<?xml version="1.0" encoding="UTF-8"?>
<ds3>
<model name="model.xml" offset="0" size="18749"/>
<asset name="canvas.png" offset="18749" size="163519"/>
<model name="model.xml" offset="0" size="18787"/>
<asset name="canvas.png" offset="18787" size="163519"/>
</ds3>
<?xml version="1.0" encoding="UTF-8"?>
<canvas originX="0.832218" originY="0.430706" originZ="2.51087" rigType="None">
<nodes>
<node id="{00fde052-9d4e-448e-9682-f5ed93434e0c}" partId="{db02c694-a3b7-4583-b79b-31c4ba352c3f}" radius="0.0182927" x="0.872962" y="0.200805" z="2.36857"/>
<node id="{091f00c6-a346-44ce-b2f0-905256644b35}" partId="{fe8c956d-6316-4f57-890a-611ab76cce26}" radius="0.0108696" x="1.00543" y="0.407609" z="2.1875"/>
<node id="{1021605a-1c1a-4266-83bb-761abfcaebd4}" partId="{fe8c956d-6316-4f57-890a-611ab76cce26}" radius="0.0353261" x="0.88587" y="0.278897" z="2.30844"/>
<node id="{18e00014-6fd3-4f46-b474-25a98ad0a5a5}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" radius="0.0271739" x="0.831522" y="0.524457" z="2.98098"/>
<node id="{1ef138ab-f1c0-416c-af05-b988dc62c9ec}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" radius="0.0597826" x="0.831522" y="0.475543" z="2.91848"/>
<node id="{3ccfd176-17d3-4e5e-9a66-7973b67a9b7d}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" radius="0.076087" x="0.831522" y="0.290761" z="2.4375"/>
<node id="{45fa4407-c043-4aba-907d-4c84d16ba5e5}" partId="{fe8c956d-6316-4f57-890a-611ab76cce26}" radius="0.0108696" x="1.00543" y="0.665761" z="2.13587"/>
<node id="{4fc24460-bb11-492b-95d5-3df726947c9a}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" radius="0.0466936" x="0.831522" y="0.228261" z="2.2962"/>
<node id="{551efaf5-ae0d-4700-9e9f-acae50c9ba96}" partId="{a159d6c3-29fe-4da9-9345-a362bbbf4cb8}" radius="0.0308874" x="0.831522" y="0.273312" z="2.18007"/>
<node id="{59f24f88-0441-418f-8d8d-4d30d4b5015b}" partId="{3436ee77-24b3-4a78-807f-82e58311a5e9}" radius="0.0108696" x="1.28533" y="0.741848" z="2.86141"/>
<node id="{5c63c4df-8f54-4fef-933a-f975973d0e0e}" partId="{584cee26-9deb-4bb1-997a-b0aca108583d}" radius="0.0127174" x="1.07609" y="0.75" z="2.66033"/>
<node id="{60de62dd-338e-425a-9b83-ed0776bb380a}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" radius="0.0163044" x="0.831522" y="0.274457" z="2.11617"/>
<node id="{67ed5a94-e749-46d1-8ccf-2b33a3e878d6}" partId="{a159d6c3-29fe-4da9-9345-a362bbbf4cb8}" radius="0.0180256" x="0.831522" y="0.345109" z="2.18328"/>
<node id="{6a47f6ac-e4a8-4e5d-9000-b4c0dfb999a3}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" radius="0.0625" x="0.831522" y="0.315217" z="2.50815"/>
<node id="{71b0f00a-6909-4121-bf1d-83b0a64eab41}" partId="{584cee26-9deb-4bb1-997a-b0aca108583d}" radius="0.0127174" x="1.21196" y="0.866848" z="2.78261"/>
<node id="{71ff2d8c-862c-4b06-aa8e-6d89afc335fd}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" radius="0.0244565" x="0.831522" y="0.277174" z="2.2337"/>
<node id="{83aaf66e-e525-42d1-b8fe-3f1772fe57ef}" partId="{3436ee77-24b3-4a78-807f-82e58311a5e9}" radius="0.0190217" x="0.869565" y="0.293478" z="2.43839"/>
<node id="{8aaa9417-0abe-4616-9ef3-01de9803cfe2}" partId="{3436ee77-24b3-4a78-807f-82e58311a5e9}" radius="0.0190217" x="1.13859" y="0.38587" z="2.46624"/>
<node id="{8e680f16-9603-40de-9a5e-90a849ff05f6}" partId="{584cee26-9deb-4bb1-997a-b0aca108583d}" radius="0.0344565" x="0.872282" y="0.260869" z="2.37976"/>
<node id="{95b3fc4e-9bab-4b12-80c0-a875649ca4aa}" partId="{fe8c956d-6316-4f57-890a-611ab76cce26}" radius="0.0108696" x="1.16033" y="0.842391" z="2.01087"/>
<node id="{96f55ae2-c07f-4637-b8b6-4b7d4c2b209a}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" radius="0.0438766" x="0.831522" y="0.271739" z="2.17935"/>
<node id="{98a8ed94-50ed-43d3-85cf-7968f5e260da}" partId="{3436ee77-24b3-4a78-807f-82e58311a5e9}" radius="0.0108696" x="0.980978" y="0.453804" z="2.78533"/>
<node id="{9b835bb3-040c-4f5f-9993-4240128e0297}" partId="{584cee26-9deb-4bb1-997a-b0aca108583d}" radius="0.0222283" x="0.948369" y="0.349185" z="2.39368"/>
<node id="{a0e15099-f840-47fe-b7cc-76e2821f82c8}" partId="{db02c694-a3b7-4583-b79b-31c4ba352c3f}" radius="0.0689289" x="0.916609" y="0.236665" z="2.64057"/>
<node id="{a64dcc04-41eb-4d71-b95a-9fb7dacc3350}" partId="{db02c694-a3b7-4583-b79b-31c4ba352c3f}" radius="0.0271739" x="0.870884" y="0.201298" z="2.41031"/>
<node id="{abfebd7d-3a35-41c8-adee-06aced895298}" partId="{fe8c956d-6316-4f57-890a-611ab76cce26}" radius="0.0108696" x="1.1087" y="0.774457" z="2.06793"/>
<node id="{ac99b0c2-942c-446e-a0bf-7210382e66a5}" partId="{a159d6c3-29fe-4da9-9345-a362bbbf4cb8}" radius="0.005" x="0.831522" y="0.559285" z="2.20502"/>
<node id="{b0c0ad56-ba00-4999-8adb-466c6d7185ba}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" radius="0.0597826" x="0.831522" y="0.32337" z="2.61141"/>
<node id="{bfb2b58d-773e-4636-a95f-f036139f6d9b}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" radius="0.0108696" x="0.831522" y="0.244565" z="2.04348"/>
<node id="{cc6c7d03-156f-455f-8456-0b6438e1f6ef}" partId="{fe8c956d-6316-4f57-890a-611ab76cce26}" radius="0.0108696" x="1.1875" y="0.869565" z="1.94293"/>
<node id="{d45a860c-f9ec-41bb-868e-3bdc3cf04e8d}" partId="{a159d6c3-29fe-4da9-9345-a362bbbf4cb8}" radius="0.0125908" x="0.831522" y="0.448867" z="2.19144"/>
<node id="{d6d1a24b-f3b8-478d-9989-feda57a2ee32}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" radius="0.0108696" x="0.831522" y="0.263587" z="2.0788"/>
<node id="{dacc2607-14f6-4c36-a20a-31d5b57d1488}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" radius="0.0597826" x="0.831522" y="0.350543" z="2.73098"/>
<node id="{deb5ae03-03f6-4e0c-a275-fa681b60b379}" partId="{584cee26-9deb-4bb1-997a-b0aca108583d}" radius="0.0127174" x="1.23098" y="0.899456" z="2.85055"/>
<node id="{df279fce-55a5-4c68-bb96-feefc9a068c8}" partId="{584cee26-9deb-4bb1-997a-b0aca108583d}" radius="0.0127174" x="0.972826" y="0.538044" z="2.55163"/>
<node id="{e1fa1f4c-8426-4dc1-afc1-3f8dee8f1641}" partId="{db02c694-a3b7-4583-b79b-31c4ba352c3f}" radius="0.107635" x="0.940221" y="0.304323" z="2.8689"/>
<node id="{e9410d86-9d17-41a5-9b55-8587d1e93ea3}" partId="{3436ee77-24b3-4a78-807f-82e58311a5e9}" radius="0.0108696" x="1.45652" y="0.913043" z="3.05163"/>
<node id="{e9d3ac47-6d17-4439-8176-9c51498f8cf0}" partId="{3436ee77-24b3-4a78-807f-82e58311a5e9}" radius="0.0108696" x="1.01359" y="0.470109" z="2.50428"/>
<node id="{f1a99c28-4dd0-45f5-9dc4-769ff13d2fda}" partId="{fe8c956d-6316-4f57-890a-611ab76cce26}" radius="0.0190217" x="1.01359" y="0.413043" z="2.35598"/>
<node id="{f2677136-fe7c-4157-bf6e-01f0f51ef4ff}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" radius="0.0597826" x="0.831522" y="0.407609" z="2.84239"/>
<node id="{f5447f26-5f45-4192-97a8-0d6cc635a19b}" partId="{3436ee77-24b3-4a78-807f-82e58311a5e9}" radius="0.0108696" x="1.5" y="0.9375" z="3.11685"/>
<node id="{f6045c8a-b651-425b-86d6-4656ed9d9081}" partId="{db02c694-a3b7-4583-b79b-31c4ba352c3f}" radius="0.0597826" x="1.01505" y="0.370967" z="3.06827"/>
<node id="{f845abe2-f311-47be-8c61-8b0fa1fa98d2}" partId="{3436ee77-24b3-4a78-807f-82e58311a5e9}" radius="0.0108696" x="1.40761" y="0.858696" z="2.96467"/>
<node id="{f9dad8ee-47b7-4fb4-aa7a-846995320792}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" radius="0.0682336" x="0.831522" y="0.214674" z="2.37228"/>
<node id="{fa1a535e-4d99-42a8-930c-2143b1d124eb}" partId="{fe8c956d-6316-4f57-890a-611ab76cce26}" radius="0.0217391" x="1.02717" y="0.342391" z="2.32065"/>
<node id="{fb6417cf-c962-41fd-ac81-20e8b427ab2e}" partId="{584cee26-9deb-4bb1-997a-b0aca108583d}" radius="0.01" x="1.17391" y="0.826087" z="2.72283"/>
<node id="{fd9f1818-8101-4850-a420-55f72567c639}" partId="{584cee26-9deb-4bb1-997a-b0aca108583d}" radius="0.0127174" x="1.02174" y="0.470109" z="2.44022"/>
<node id="{fdfccd5b-e3a1-4ed3-8e26-2db335c6b683}" partId="{584cee26-9deb-4bb1-997a-b0aca108583d}" radius="0.0188359" x="1.1087" y="0.380435" z="2.40761"/>
<node id="{00fde052-9d4e-448e-9682-f5ed93434e0c}" partId="{2c7dcbf9-1455-49f4-9a7c-7ddb17c2179d}" radius="0.0182927" x="0.872962" y="0.190653" z="2.37365"/>
<node id="{091f00c6-a346-44ce-b2f0-905256644b35}" partId="{a5a129b6-564f-46cb-8309-cf05419aa7ed}" radius="0.0108696" x="1.00543" y="0.407609" z="2.1875"/>
<node id="{1021605a-1c1a-4266-83bb-761abfcaebd4}" partId="{a5a129b6-564f-46cb-8309-cf05419aa7ed}" radius="0.0353261" x="0.88587" y="0.278897" z="2.30844"/>
<node id="{18e00014-6fd3-4f46-b474-25a98ad0a5a5}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" radius="0.0271739" x="0.831522" y="0.524457" z="2.98098"/>
<node id="{1ef138ab-f1c0-416c-af05-b988dc62c9ec}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" radius="0.0597826" x="0.831522" y="0.475543" z="2.91848"/>
<node id="{3ccfd176-17d3-4e5e-9a66-7973b67a9b7d}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" radius="0.076087" x="0.831522" y="0.290761" z="2.4375"/>
<node id="{45fa4407-c043-4aba-907d-4c84d16ba5e5}" partId="{a5a129b6-564f-46cb-8309-cf05419aa7ed}" radius="0.0108696" x="1.00543" y="0.665761" z="2.13587"/>
<node id="{4fc24460-bb11-492b-95d5-3df726947c9a}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" radius="0.0466936" x="0.831522" y="0.228261" z="2.2962"/>
<node id="{551efaf5-ae0d-4700-9e9f-acae50c9ba96}" partId="{0ec19fd0-d226-4ba3-8cda-0c444a398038}" radius="0.0308874" x="0.831522" y="0.273312" z="2.18007"/>
<node id="{59f24f88-0441-418f-8d8d-4d30d4b5015b}" partId="{3875e962-9c33-4214-9049-34a042e653ad}" radius="0.0108696" x="1.28533" y="0.741848" z="2.86141"/>
<node id="{5c63c4df-8f54-4fef-933a-f975973d0e0e}" partId="{57488f4d-0076-4e58-94c5-9ff29d94a77f}" radius="0.0127174" x="1.07609" y="0.75" z="2.66033"/>
<node id="{60de62dd-338e-425a-9b83-ed0776bb380a}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" radius="0.0163044" x="0.831522" y="0.274457" z="2.11617"/>
<node id="{67ed5a94-e749-46d1-8ccf-2b33a3e878d6}" partId="{0ec19fd0-d226-4ba3-8cda-0c444a398038}" radius="0.0180256" x="0.831522" y="0.345109" z="2.18328"/>
<node id="{6a47f6ac-e4a8-4e5d-9000-b4c0dfb999a3}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" radius="0.0625" x="0.831522" y="0.315217" z="2.50815"/>
<node id="{71b0f00a-6909-4121-bf1d-83b0a64eab41}" partId="{57488f4d-0076-4e58-94c5-9ff29d94a77f}" radius="0.0127174" x="1.21196" y="0.866848" z="2.78261"/>
<node id="{71ff2d8c-862c-4b06-aa8e-6d89afc335fd}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" radius="0.0244565" x="0.831522" y="0.277174" z="2.2337"/>
<node id="{83aaf66e-e525-42d1-b8fe-3f1772fe57ef}" partId="{3875e962-9c33-4214-9049-34a042e653ad}" radius="0.0190217" x="0.869565" y="0.293478" z="2.43839"/>
<node id="{8aaa9417-0abe-4616-9ef3-01de9803cfe2}" partId="{3875e962-9c33-4214-9049-34a042e653ad}" radius="0.0190217" x="1.13859" y="0.38587" z="2.46624"/>
<node id="{8e680f16-9603-40de-9a5e-90a849ff05f6}" partId="{57488f4d-0076-4e58-94c5-9ff29d94a77f}" radius="0.0344565" x="0.872282" y="0.260869" z="2.37976"/>
<node id="{95b3fc4e-9bab-4b12-80c0-a875649ca4aa}" partId="{a5a129b6-564f-46cb-8309-cf05419aa7ed}" radius="0.0108696" x="1.16033" y="0.842391" z="2.01087"/>
<node id="{96f55ae2-c07f-4637-b8b6-4b7d4c2b209a}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" radius="0.0438766" x="0.831522" y="0.271739" z="2.17935"/>
<node id="{98a8ed94-50ed-43d3-85cf-7968f5e260da}" partId="{3875e962-9c33-4214-9049-34a042e653ad}" radius="0.0108696" x="0.980978" y="0.453804" z="2.78533"/>
<node id="{9b835bb3-040c-4f5f-9993-4240128e0297}" partId="{57488f4d-0076-4e58-94c5-9ff29d94a77f}" radius="0.0222283" x="0.948369" y="0.349185" z="2.39368"/>
<node id="{a0e15099-f840-47fe-b7cc-76e2821f82c8}" partId="{2c7dcbf9-1455-49f4-9a7c-7ddb17c2179d}" radius="0.0689289" x="0.916609" y="0.226513" z="2.64565"/>
<node id="{a64dcc04-41eb-4d71-b95a-9fb7dacc3350}" partId="{2c7dcbf9-1455-49f4-9a7c-7ddb17c2179d}" radius="0.0271739" x="0.870884" y="0.191146" z="2.41539"/>
<node id="{abfebd7d-3a35-41c8-adee-06aced895298}" partId="{a5a129b6-564f-46cb-8309-cf05419aa7ed}" radius="0.0108696" x="1.1087" y="0.774457" z="2.06793"/>
<node id="{ac99b0c2-942c-446e-a0bf-7210382e66a5}" partId="{0ec19fd0-d226-4ba3-8cda-0c444a398038}" radius="0.005" x="0.831522" y="0.559285" z="2.20502"/>
<node id="{b0c0ad56-ba00-4999-8adb-466c6d7185ba}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" radius="0.0597826" x="0.831522" y="0.32337" z="2.61141"/>
<node id="{bfb2b58d-773e-4636-a95f-f036139f6d9b}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" radius="0.0108696" x="0.831522" y="0.244565" z="2.04348"/>
<node id="{cc6c7d03-156f-455f-8456-0b6438e1f6ef}" partId="{a5a129b6-564f-46cb-8309-cf05419aa7ed}" radius="0.0108696" x="1.1875" y="0.869565" z="1.94293"/>
<node id="{d45a860c-f9ec-41bb-868e-3bdc3cf04e8d}" partId="{0ec19fd0-d226-4ba3-8cda-0c444a398038}" radius="0.0125908" x="0.831522" y="0.448867" z="2.19144"/>
<node id="{d6d1a24b-f3b8-478d-9989-feda57a2ee32}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" radius="0.0108696" x="0.831522" y="0.263587" z="2.0788"/>
<node id="{dacc2607-14f6-4c36-a20a-31d5b57d1488}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" radius="0.0597826" x="0.831522" y="0.350543" z="2.73098"/>
<node id="{deb5ae03-03f6-4e0c-a275-fa681b60b379}" partId="{57488f4d-0076-4e58-94c5-9ff29d94a77f}" radius="0.0127174" x="1.23098" y="0.899456" z="2.85055"/>
<node id="{df279fce-55a5-4c68-bb96-feefc9a068c8}" partId="{57488f4d-0076-4e58-94c5-9ff29d94a77f}" radius="0.0127174" x="0.972826" y="0.538044" z="2.55163"/>
<node id="{e1fa1f4c-8426-4dc1-afc1-3f8dee8f1641}" partId="{2c7dcbf9-1455-49f4-9a7c-7ddb17c2179d}" radius="0.107635" x="0.940221" y="0.294171" z="2.87398"/>
<node id="{e9410d86-9d17-41a5-9b55-8587d1e93ea3}" partId="{3875e962-9c33-4214-9049-34a042e653ad}" radius="0.0108696" x="1.45652" y="0.913043" z="3.05163"/>
<node id="{e9d3ac47-6d17-4439-8176-9c51498f8cf0}" partId="{3875e962-9c33-4214-9049-34a042e653ad}" radius="0.0108696" x="1.01359" y="0.470109" z="2.50428"/>
<node id="{f1a99c28-4dd0-45f5-9dc4-769ff13d2fda}" partId="{a5a129b6-564f-46cb-8309-cf05419aa7ed}" radius="0.0190217" x="1.01359" y="0.413043" z="2.35598"/>
<node id="{f2677136-fe7c-4157-bf6e-01f0f51ef4ff}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" radius="0.0597826" x="0.831522" y="0.407609" z="2.84239"/>
<node id="{f5447f26-5f45-4192-97a8-0d6cc635a19b}" partId="{3875e962-9c33-4214-9049-34a042e653ad}" radius="0.0108696" x="1.5" y="0.9375" z="3.11685"/>
<node id="{f6045c8a-b651-425b-86d6-4656ed9d9081}" partId="{2c7dcbf9-1455-49f4-9a7c-7ddb17c2179d}" radius="0.0597826" x="1.01505" y="0.360815" z="3.07335"/>
<node id="{f845abe2-f311-47be-8c61-8b0fa1fa98d2}" partId="{3875e962-9c33-4214-9049-34a042e653ad}" radius="0.0108696" x="1.40761" y="0.858696" z="2.96467"/>
<node id="{f9dad8ee-47b7-4fb4-aa7a-846995320792}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" radius="0.0682336" x="0.831522" y="0.214674" z="2.37228"/>
<node id="{fa1a535e-4d99-42a8-930c-2143b1d124eb}" partId="{a5a129b6-564f-46cb-8309-cf05419aa7ed}" radius="0.0217391" x="1.02717" y="0.342391" z="2.32065"/>
<node id="{fb6417cf-c962-41fd-ac81-20e8b427ab2e}" partId="{57488f4d-0076-4e58-94c5-9ff29d94a77f}" radius="0.01" x="1.17391" y="0.826087" z="2.72283"/>
<node id="{fd9f1818-8101-4850-a420-55f72567c639}" partId="{57488f4d-0076-4e58-94c5-9ff29d94a77f}" radius="0.0127174" x="1.02174" y="0.470109" z="2.44022"/>
<node id="{fdfccd5b-e3a1-4ed3-8e26-2db335c6b683}" partId="{57488f4d-0076-4e58-94c5-9ff29d94a77f}" radius="0.0188359" x="1.1087" y="0.380435" z="2.40761"/>
</nodes>
<edges>
<edge from="{60de62dd-338e-425a-9b83-ed0776bb380a}" id="{004e3284-50b1-4c43-ae03-351057f72d34}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" to="{d6d1a24b-f3b8-478d-9989-feda57a2ee32}"/>
<edge from="{1021605a-1c1a-4266-83bb-761abfcaebd4}" id="{0569f362-f47c-485c-a6a9-6e3481b21a90}" partId="{fe8c956d-6316-4f57-890a-611ab76cce26}" to="{fa1a535e-4d99-42a8-930c-2143b1d124eb}"/>
<edge from="{f1a99c28-4dd0-45f5-9dc4-769ff13d2fda}" id="{0a8be92c-5977-4471-b2d3-5009eda7df2e}" partId="{fe8c956d-6316-4f57-890a-611ab76cce26}" to="{091f00c6-a346-44ce-b2f0-905256644b35}"/>
<edge from="{5c63c4df-8f54-4fef-933a-f975973d0e0e}" id="{0b30676d-4c10-4c4b-aa73-04a7a42a1b7f}" partId="{584cee26-9deb-4bb1-997a-b0aca108583d}" to="{fb6417cf-c962-41fd-ac81-20e8b427ab2e}"/>
<edge from="{df279fce-55a5-4c68-bb96-feefc9a068c8}" id="{0b6366a7-a16e-4f6f-a0f7-13d1dfa5f591}" partId="{584cee26-9deb-4bb1-997a-b0aca108583d}" to="{5c63c4df-8f54-4fef-933a-f975973d0e0e}"/>
<edge from="{b0c0ad56-ba00-4999-8adb-466c6d7185ba}" id="{0d2b5542-fbb8-4aa9-a0cb-c2517dcf1aa9}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" to="{dacc2607-14f6-4c36-a20a-31d5b57d1488}"/>
<edge from="{96f55ae2-c07f-4637-b8b6-4b7d4c2b209a}" id="{0e321006-a9ad-4bd1-9429-80e9b4781c3e}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" to="{71ff2d8c-862c-4b06-aa8e-6d89afc335fd}"/>
<edge from="{67ed5a94-e749-46d1-8ccf-2b33a3e878d6}" id="{10582139-843f-4591-bc63-1e9ae19c6d42}" partId="{a159d6c3-29fe-4da9-9345-a362bbbf4cb8}" to="{d45a860c-f9ec-41bb-868e-3bdc3cf04e8d}"/>
<edge from="{45fa4407-c043-4aba-907d-4c84d16ba5e5}" id="{22253314-b363-4698-a072-c0a5ba0ac000}" partId="{fe8c956d-6316-4f57-890a-611ab76cce26}" to="{abfebd7d-3a35-41c8-adee-06aced895298}"/>
<edge from="{f9dad8ee-47b7-4fb4-aa7a-846995320792}" id="{23fcdae1-af2b-4420-a761-63f3d0d203b4}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" to="{3ccfd176-17d3-4e5e-9a66-7973b67a9b7d}"/>
<edge from="{8aaa9417-0abe-4616-9ef3-01de9803cfe2}" id="{35fca7ae-1411-4125-a29d-c977e60c2026}" partId="{3436ee77-24b3-4a78-807f-82e58311a5e9}" to="{e9d3ac47-6d17-4439-8176-9c51498f8cf0}"/>
<edge from="{fdfccd5b-e3a1-4ed3-8e26-2db335c6b683}" id="{3a69636f-46a5-4363-88a3-afa75c303fb7}" partId="{584cee26-9deb-4bb1-997a-b0aca108583d}" to="{fd9f1818-8101-4850-a420-55f72567c639}"/>
<edge from="{f2677136-fe7c-4157-bf6e-01f0f51ef4ff}" id="{3af2982e-d86f-492c-b9e0-ef4f5c0af53d}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" to="{1ef138ab-f1c0-416c-af05-b988dc62c9ec}"/>
<edge from="{d45a860c-f9ec-41bb-868e-3bdc3cf04e8d}" id="{3d874434-2913-4166-8e6e-712a47e44db0}" partId="{a159d6c3-29fe-4da9-9345-a362bbbf4cb8}" to="{ac99b0c2-942c-446e-a0bf-7210382e66a5}"/>
<edge from="{f845abe2-f311-47be-8c61-8b0fa1fa98d2}" id="{4dfeb7b4-4426-418f-b85f-c14da4d296f2}" partId="{3436ee77-24b3-4a78-807f-82e58311a5e9}" to="{e9410d86-9d17-41a5-9b55-8587d1e93ea3}"/>
<edge from="{6a47f6ac-e4a8-4e5d-9000-b4c0dfb999a3}" id="{566cd759-190b-4374-b57d-1ef8ed54cb45}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" to="{b0c0ad56-ba00-4999-8adb-466c6d7185ba}"/>
<edge from="{dacc2607-14f6-4c36-a20a-31d5b57d1488}" id="{5b0805fc-6b71-4a8f-9318-4dd3df63f718}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" to="{f2677136-fe7c-4157-bf6e-01f0f51ef4ff}"/>
<edge from="{a64dcc04-41eb-4d71-b95a-9fb7dacc3350}" id="{63dac02d-b2f8-4ebc-98c4-54fcea0d9798}" partId="{db02c694-a3b7-4583-b79b-31c4ba352c3f}" to="{00fde052-9d4e-448e-9682-f5ed93434e0c}"/>
<edge from="{60de62dd-338e-425a-9b83-ed0776bb380a}" id="{67bd7a5e-4002-418c-ad41-f8827056d936}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" to="{96f55ae2-c07f-4637-b8b6-4b7d4c2b209a}"/>
<edge from="{95b3fc4e-9bab-4b12-80c0-a875649ca4aa}" id="{67d3a132-f6f4-4c39-93fd-53d66a5df4ab}" partId="{fe8c956d-6316-4f57-890a-611ab76cce26}" to="{cc6c7d03-156f-455f-8456-0b6438e1f6ef}"/>
<edge from="{a0e15099-f840-47fe-b7cc-76e2821f82c8}" id="{6cc8a10c-58e8-4833-98f3-d1bba4461f34}" partId="{db02c694-a3b7-4583-b79b-31c4ba352c3f}" to="{e1fa1f4c-8426-4dc1-afc1-3f8dee8f1641}"/>
<edge from="{a64dcc04-41eb-4d71-b95a-9fb7dacc3350}" id="{720d02e7-45a3-4659-a779-2b775f58d61f}" partId="{db02c694-a3b7-4583-b79b-31c4ba352c3f}" to="{a0e15099-f840-47fe-b7cc-76e2821f82c8}"/>
<edge from="{e9410d86-9d17-41a5-9b55-8587d1e93ea3}" id="{748ecd4b-ecaa-4707-9d1b-eba56429b77a}" partId="{3436ee77-24b3-4a78-807f-82e58311a5e9}" to="{f5447f26-5f45-4192-97a8-0d6cc635a19b}"/>
<edge from="{e1fa1f4c-8426-4dc1-afc1-3f8dee8f1641}" id="{76dd8ca8-896a-4899-acff-c60f42725d45}" partId="{db02c694-a3b7-4583-b79b-31c4ba352c3f}" to="{f6045c8a-b651-425b-86d6-4656ed9d9081}"/>
<edge from="{091f00c6-a346-44ce-b2f0-905256644b35}" id="{7cd9de86-8db5-4672-9d73-e813f5cad9a9}" partId="{fe8c956d-6316-4f57-890a-611ab76cce26}" to="{45fa4407-c043-4aba-907d-4c84d16ba5e5}"/>
<edge from="{abfebd7d-3a35-41c8-adee-06aced895298}" id="{7de5e8e0-24ff-4b64-9047-4bd8f4855506}" partId="{fe8c956d-6316-4f57-890a-611ab76cce26}" to="{95b3fc4e-9bab-4b12-80c0-a875649ca4aa}"/>
<edge from="{8e680f16-9603-40de-9a5e-90a849ff05f6}" id="{8a744155-4a9f-446d-a39e-713fdd134426}" partId="{584cee26-9deb-4bb1-997a-b0aca108583d}" to="{9b835bb3-040c-4f5f-9993-4240128e0297}"/>
<edge from="{fa1a535e-4d99-42a8-930c-2143b1d124eb}" id="{a439f439-4dba-488b-b4ee-d38b19fc4fcf}" partId="{fe8c956d-6316-4f57-890a-611ab76cce26}" to="{f1a99c28-4dd0-45f5-9dc4-769ff13d2fda}"/>
<edge from="{fd9f1818-8101-4850-a420-55f72567c639}" id="{a6357a12-14ba-4dc4-846b-4be892746e6a}" partId="{584cee26-9deb-4bb1-997a-b0aca108583d}" to="{df279fce-55a5-4c68-bb96-feefc9a068c8}"/>
<edge from="{71ff2d8c-862c-4b06-aa8e-6d89afc335fd}" id="{ab708822-ad92-4977-b209-b1103a334e82}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" to="{4fc24460-bb11-492b-95d5-3df726947c9a}"/>
<edge from="{1ef138ab-f1c0-416c-af05-b988dc62c9ec}" id="{b17d6b41-898c-46db-8d88-1117bc53c627}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" to="{18e00014-6fd3-4f46-b474-25a98ad0a5a5}"/>
<edge from="{71b0f00a-6909-4121-bf1d-83b0a64eab41}" id="{c232a9eb-fda5-4938-82da-c807c021b102}" partId="{584cee26-9deb-4bb1-997a-b0aca108583d}" to="{deb5ae03-03f6-4e0c-a275-fa681b60b379}"/>
<edge from="{9b835bb3-040c-4f5f-9993-4240128e0297}" id="{caaaf017-8034-4deb-ac06-b5052603bd6e}" partId="{584cee26-9deb-4bb1-997a-b0aca108583d}" to="{fdfccd5b-e3a1-4ed3-8e26-2db335c6b683}"/>
<edge from="{d6d1a24b-f3b8-478d-9989-feda57a2ee32}" id="{cb1c1ed1-eb5b-4622-b491-2b454836600b}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" to="{bfb2b58d-773e-4636-a95f-f036139f6d9b}"/>
<edge from="{83aaf66e-e525-42d1-b8fe-3f1772fe57ef}" id="{cb880891-b7e8-4592-ab80-fd165c70eccb}" partId="{3436ee77-24b3-4a78-807f-82e58311a5e9}" to="{8aaa9417-0abe-4616-9ef3-01de9803cfe2}"/>
<edge from="{98a8ed94-50ed-43d3-85cf-7968f5e260da}" id="{d102fa2e-afb6-4f0c-910a-2952dea4781c}" partId="{3436ee77-24b3-4a78-807f-82e58311a5e9}" to="{59f24f88-0441-418f-8d8d-4d30d4b5015b}"/>
<edge from="{e9d3ac47-6d17-4439-8176-9c51498f8cf0}" id="{d55b2dbc-4c3a-4c52-9c2c-16127e00a889}" partId="{3436ee77-24b3-4a78-807f-82e58311a5e9}" to="{98a8ed94-50ed-43d3-85cf-7968f5e260da}"/>
<edge from="{3ccfd176-17d3-4e5e-9a66-7973b67a9b7d}" id="{d89420d6-ffca-4dd4-a5b5-2ff9b8618b01}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" to="{6a47f6ac-e4a8-4e5d-9000-b4c0dfb999a3}"/>
<edge from="{67ed5a94-e749-46d1-8ccf-2b33a3e878d6}" id="{e99f5404-7dce-4770-aac9-22ad610b4420}" partId="{a159d6c3-29fe-4da9-9345-a362bbbf4cb8}" to="{551efaf5-ae0d-4700-9e9f-acae50c9ba96}"/>
<edge from="{59f24f88-0441-418f-8d8d-4d30d4b5015b}" id="{ee50292f-d21a-4ba6-8e31-d2f9fb577398}" partId="{3436ee77-24b3-4a78-807f-82e58311a5e9}" to="{f845abe2-f311-47be-8c61-8b0fa1fa98d2}"/>
<edge from="{fb6417cf-c962-41fd-ac81-20e8b427ab2e}" id="{f1d56833-ff9a-44d7-a337-76eb68549678}" partId="{584cee26-9deb-4bb1-997a-b0aca108583d}" to="{71b0f00a-6909-4121-bf1d-83b0a64eab41}"/>
<edge from="{4fc24460-bb11-492b-95d5-3df726947c9a}" id="{fc919b1d-e12b-42d0-a3af-fc36c8c78ef4}" partId="{209450cc-2bad-438d-9918-b40d2ca20f3e}" to="{f9dad8ee-47b7-4fb4-aa7a-846995320792}"/>
<edge from="{60de62dd-338e-425a-9b83-ed0776bb380a}" id="{004e3284-50b1-4c43-ae03-351057f72d34}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" to="{d6d1a24b-f3b8-478d-9989-feda57a2ee32}"/>
<edge from="{1021605a-1c1a-4266-83bb-761abfcaebd4}" id="{0569f362-f47c-485c-a6a9-6e3481b21a90}" partId="{a5a129b6-564f-46cb-8309-cf05419aa7ed}" to="{fa1a535e-4d99-42a8-930c-2143b1d124eb}"/>
<edge from="{f1a99c28-4dd0-45f5-9dc4-769ff13d2fda}" id="{0a8be92c-5977-4471-b2d3-5009eda7df2e}" partId="{a5a129b6-564f-46cb-8309-cf05419aa7ed}" to="{091f00c6-a346-44ce-b2f0-905256644b35}"/>
<edge from="{5c63c4df-8f54-4fef-933a-f975973d0e0e}" id="{0b30676d-4c10-4c4b-aa73-04a7a42a1b7f}" partId="{57488f4d-0076-4e58-94c5-9ff29d94a77f}" to="{fb6417cf-c962-41fd-ac81-20e8b427ab2e}"/>
<edge from="{df279fce-55a5-4c68-bb96-feefc9a068c8}" id="{0b6366a7-a16e-4f6f-a0f7-13d1dfa5f591}" partId="{57488f4d-0076-4e58-94c5-9ff29d94a77f}" to="{5c63c4df-8f54-4fef-933a-f975973d0e0e}"/>
<edge from="{b0c0ad56-ba00-4999-8adb-466c6d7185ba}" id="{0d2b5542-fbb8-4aa9-a0cb-c2517dcf1aa9}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" to="{dacc2607-14f6-4c36-a20a-31d5b57d1488}"/>
<edge from="{96f55ae2-c07f-4637-b8b6-4b7d4c2b209a}" id="{0e321006-a9ad-4bd1-9429-80e9b4781c3e}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" to="{71ff2d8c-862c-4b06-aa8e-6d89afc335fd}"/>
<edge from="{67ed5a94-e749-46d1-8ccf-2b33a3e878d6}" id="{10582139-843f-4591-bc63-1e9ae19c6d42}" partId="{0ec19fd0-d226-4ba3-8cda-0c444a398038}" to="{d45a860c-f9ec-41bb-868e-3bdc3cf04e8d}"/>
<edge from="{45fa4407-c043-4aba-907d-4c84d16ba5e5}" id="{22253314-b363-4698-a072-c0a5ba0ac000}" partId="{a5a129b6-564f-46cb-8309-cf05419aa7ed}" to="{abfebd7d-3a35-41c8-adee-06aced895298}"/>
<edge from="{f9dad8ee-47b7-4fb4-aa7a-846995320792}" id="{23fcdae1-af2b-4420-a761-63f3d0d203b4}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" to="{3ccfd176-17d3-4e5e-9a66-7973b67a9b7d}"/>
<edge from="{8aaa9417-0abe-4616-9ef3-01de9803cfe2}" id="{35fca7ae-1411-4125-a29d-c977e60c2026}" partId="{3875e962-9c33-4214-9049-34a042e653ad}" to="{e9d3ac47-6d17-4439-8176-9c51498f8cf0}"/>
<edge from="{fdfccd5b-e3a1-4ed3-8e26-2db335c6b683}" id="{3a69636f-46a5-4363-88a3-afa75c303fb7}" partId="{57488f4d-0076-4e58-94c5-9ff29d94a77f}" to="{fd9f1818-8101-4850-a420-55f72567c639}"/>
<edge from="{f2677136-fe7c-4157-bf6e-01f0f51ef4ff}" id="{3af2982e-d86f-492c-b9e0-ef4f5c0af53d}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" to="{1ef138ab-f1c0-416c-af05-b988dc62c9ec}"/>
<edge from="{d45a860c-f9ec-41bb-868e-3bdc3cf04e8d}" id="{3d874434-2913-4166-8e6e-712a47e44db0}" partId="{0ec19fd0-d226-4ba3-8cda-0c444a398038}" to="{ac99b0c2-942c-446e-a0bf-7210382e66a5}"/>
<edge from="{f845abe2-f311-47be-8c61-8b0fa1fa98d2}" id="{4dfeb7b4-4426-418f-b85f-c14da4d296f2}" partId="{3875e962-9c33-4214-9049-34a042e653ad}" to="{e9410d86-9d17-41a5-9b55-8587d1e93ea3}"/>
<edge from="{6a47f6ac-e4a8-4e5d-9000-b4c0dfb999a3}" id="{566cd759-190b-4374-b57d-1ef8ed54cb45}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" to="{b0c0ad56-ba00-4999-8adb-466c6d7185ba}"/>
<edge from="{dacc2607-14f6-4c36-a20a-31d5b57d1488}" id="{5b0805fc-6b71-4a8f-9318-4dd3df63f718}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" to="{f2677136-fe7c-4157-bf6e-01f0f51ef4ff}"/>
<edge from="{a64dcc04-41eb-4d71-b95a-9fb7dacc3350}" id="{63dac02d-b2f8-4ebc-98c4-54fcea0d9798}" partId="{2c7dcbf9-1455-49f4-9a7c-7ddb17c2179d}" to="{00fde052-9d4e-448e-9682-f5ed93434e0c}"/>
<edge from="{60de62dd-338e-425a-9b83-ed0776bb380a}" id="{67bd7a5e-4002-418c-ad41-f8827056d936}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" to="{96f55ae2-c07f-4637-b8b6-4b7d4c2b209a}"/>
<edge from="{95b3fc4e-9bab-4b12-80c0-a875649ca4aa}" id="{67d3a132-f6f4-4c39-93fd-53d66a5df4ab}" partId="{a5a129b6-564f-46cb-8309-cf05419aa7ed}" to="{cc6c7d03-156f-455f-8456-0b6438e1f6ef}"/>
<edge from="{a0e15099-f840-47fe-b7cc-76e2821f82c8}" id="{6cc8a10c-58e8-4833-98f3-d1bba4461f34}" partId="{2c7dcbf9-1455-49f4-9a7c-7ddb17c2179d}" to="{e1fa1f4c-8426-4dc1-afc1-3f8dee8f1641}"/>
<edge from="{a64dcc04-41eb-4d71-b95a-9fb7dacc3350}" id="{720d02e7-45a3-4659-a779-2b775f58d61f}" partId="{2c7dcbf9-1455-49f4-9a7c-7ddb17c2179d}" to="{a0e15099-f840-47fe-b7cc-76e2821f82c8}"/>
<edge from="{e9410d86-9d17-41a5-9b55-8587d1e93ea3}" id="{748ecd4b-ecaa-4707-9d1b-eba56429b77a}" partId="{3875e962-9c33-4214-9049-34a042e653ad}" to="{f5447f26-5f45-4192-97a8-0d6cc635a19b}"/>
<edge from="{e1fa1f4c-8426-4dc1-afc1-3f8dee8f1641}" id="{76dd8ca8-896a-4899-acff-c60f42725d45}" partId="{2c7dcbf9-1455-49f4-9a7c-7ddb17c2179d}" to="{f6045c8a-b651-425b-86d6-4656ed9d9081}"/>
<edge from="{091f00c6-a346-44ce-b2f0-905256644b35}" id="{7cd9de86-8db5-4672-9d73-e813f5cad9a9}" partId="{a5a129b6-564f-46cb-8309-cf05419aa7ed}" to="{45fa4407-c043-4aba-907d-4c84d16ba5e5}"/>
<edge from="{abfebd7d-3a35-41c8-adee-06aced895298}" id="{7de5e8e0-24ff-4b64-9047-4bd8f4855506}" partId="{a5a129b6-564f-46cb-8309-cf05419aa7ed}" to="{95b3fc4e-9bab-4b12-80c0-a875649ca4aa}"/>
<edge from="{8e680f16-9603-40de-9a5e-90a849ff05f6}" id="{8a744155-4a9f-446d-a39e-713fdd134426}" partId="{57488f4d-0076-4e58-94c5-9ff29d94a77f}" to="{9b835bb3-040c-4f5f-9993-4240128e0297}"/>
<edge from="{fa1a535e-4d99-42a8-930c-2143b1d124eb}" id="{a439f439-4dba-488b-b4ee-d38b19fc4fcf}" partId="{a5a129b6-564f-46cb-8309-cf05419aa7ed}" to="{f1a99c28-4dd0-45f5-9dc4-769ff13d2fda}"/>
<edge from="{fd9f1818-8101-4850-a420-55f72567c639}" id="{a6357a12-14ba-4dc4-846b-4be892746e6a}" partId="{57488f4d-0076-4e58-94c5-9ff29d94a77f}" to="{df279fce-55a5-4c68-bb96-feefc9a068c8}"/>
<edge from="{71ff2d8c-862c-4b06-aa8e-6d89afc335fd}" id="{ab708822-ad92-4977-b209-b1103a334e82}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" to="{4fc24460-bb11-492b-95d5-3df726947c9a}"/>
<edge from="{1ef138ab-f1c0-416c-af05-b988dc62c9ec}" id="{b17d6b41-898c-46db-8d88-1117bc53c627}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" to="{18e00014-6fd3-4f46-b474-25a98ad0a5a5}"/>
<edge from="{71b0f00a-6909-4121-bf1d-83b0a64eab41}" id="{c232a9eb-fda5-4938-82da-c807c021b102}" partId="{57488f4d-0076-4e58-94c5-9ff29d94a77f}" to="{deb5ae03-03f6-4e0c-a275-fa681b60b379}"/>
<edge from="{9b835bb3-040c-4f5f-9993-4240128e0297}" id="{caaaf017-8034-4deb-ac06-b5052603bd6e}" partId="{57488f4d-0076-4e58-94c5-9ff29d94a77f}" to="{fdfccd5b-e3a1-4ed3-8e26-2db335c6b683}"/>
<edge from="{d6d1a24b-f3b8-478d-9989-feda57a2ee32}" id="{cb1c1ed1-eb5b-4622-b491-2b454836600b}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" to="{bfb2b58d-773e-4636-a95f-f036139f6d9b}"/>
<edge from="{83aaf66e-e525-42d1-b8fe-3f1772fe57ef}" id="{cb880891-b7e8-4592-ab80-fd165c70eccb}" partId="{3875e962-9c33-4214-9049-34a042e653ad}" to="{8aaa9417-0abe-4616-9ef3-01de9803cfe2}"/>
<edge from="{98a8ed94-50ed-43d3-85cf-7968f5e260da}" id="{d102fa2e-afb6-4f0c-910a-2952dea4781c}" partId="{3875e962-9c33-4214-9049-34a042e653ad}" to="{59f24f88-0441-418f-8d8d-4d30d4b5015b}"/>
<edge from="{e9d3ac47-6d17-4439-8176-9c51498f8cf0}" id="{d55b2dbc-4c3a-4c52-9c2c-16127e00a889}" partId="{3875e962-9c33-4214-9049-34a042e653ad}" to="{98a8ed94-50ed-43d3-85cf-7968f5e260da}"/>
<edge from="{3ccfd176-17d3-4e5e-9a66-7973b67a9b7d}" id="{d89420d6-ffca-4dd4-a5b5-2ff9b8618b01}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" to="{6a47f6ac-e4a8-4e5d-9000-b4c0dfb999a3}"/>
<edge from="{67ed5a94-e749-46d1-8ccf-2b33a3e878d6}" id="{e99f5404-7dce-4770-aac9-22ad610b4420}" partId="{0ec19fd0-d226-4ba3-8cda-0c444a398038}" to="{551efaf5-ae0d-4700-9e9f-acae50c9ba96}"/>
<edge from="{59f24f88-0441-418f-8d8d-4d30d4b5015b}" id="{ee50292f-d21a-4ba6-8e31-d2f9fb577398}" partId="{3875e962-9c33-4214-9049-34a042e653ad}" to="{f845abe2-f311-47be-8c61-8b0fa1fa98d2}"/>
<edge from="{fb6417cf-c962-41fd-ac81-20e8b427ab2e}" id="{f1d56833-ff9a-44d7-a337-76eb68549678}" partId="{57488f4d-0076-4e58-94c5-9ff29d94a77f}" to="{71b0f00a-6909-4121-bf1d-83b0a64eab41}"/>
<edge from="{4fc24460-bb11-492b-95d5-3df726947c9a}" id="{fc919b1d-e12b-42d0-a3af-fc36c8c78ef4}" partId="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" to="{f9dad8ee-47b7-4fb4-aa7a-846995320792}"/>
</edges>
<parts>
<part chamfered="false" color="#ffaeb0b0" countershaded="true" disabled="false" id="{209450cc-2bad-438d-9918-b40d2ca20f3e}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffaeb0b0" disabled="false" id="{3436ee77-24b3-4a78-807f-82e58311a5e9}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="true"/>
<part chamfered="false" color="#ffaeb0b0" disabled="false" id="{584cee26-9deb-4bb1-997a-b0aca108583d}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="true"/>
<part chamfered="false" color="#ffaeb0b0" countershaded="true" disabled="false" id="{a159d6c3-29fe-4da9-9345-a362bbbf4cb8}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#c5aeb0b0" cutRotation="-0.44" deformWidth="0.2" disabled="false" id="{db02c694-a3b7-4583-b79b-31c4ba352c3f}" locked="false" rounded="true" subdived="false" visible="true" xMirrored="true"/>
<part chamfered="false" color="#ffaeb0b0" disabled="false" id="{fe8c956d-6316-4f57-890a-611ab76cce26}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="true"/>
<part chamfered="false" color="#ffaeb0b0" countershaded="true" disabled="false" id="{0ec19fd0-d226-4ba3-8cda-0c444a398038}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/>
<part base="Average" chamfered="false" color="#c5aeb0b0" cutRotation="-0.44" deformUnified="true" deformWidth="0.11" disabled="false" id="{2c7dcbf9-1455-49f4-9a7c-7ddb17c2179d}" locked="false" rounded="true" subdived="false" visible="true" xMirrored="true"/>
<part chamfered="false" color="#ffaeb0b0" disabled="false" id="{3875e962-9c33-4214-9049-34a042e653ad}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="true"/>
<part chamfered="false" color="#ffaeb0b0" disabled="false" id="{57488f4d-0076-4e58-94c5-9ff29d94a77f}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="true"/>
<part chamfered="false" color="#ffaeb0b0" countershaded="true" disabled="false" id="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" locked="false" rounded="true" subdived="true" visible="true" xMirrored="false"/>
<part chamfered="false" color="#ffaeb0b0" disabled="false" id="{a5a129b6-564f-46cb-8309-cf05419aa7ed}" locked="false" rounded="false" subdived="false" visible="true" xMirrored="true"/>
</parts>
<components>
<component combineMode="Normal" expanded="false" id="{5def49b6-55e8-4204-8afc-6a366b6ac692}" linkData="{fe8c956d-6316-4f57-890a-611ab76cce26}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{6b5d864d-5917-432b-8a29-07cde8feab46}" linkData="{584cee26-9deb-4bb1-997a-b0aca108583d}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{b51a7b5d-44d6-4983-be73-037a49e3e2c7}" linkData="{3436ee77-24b3-4a78-807f-82e58311a5e9}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{cd959326-2f15-4cf9-8caf-44d0ed14e161}" linkData="{db02c694-a3b7-4583-b79b-31c4ba352c3f}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{ae39225c-5609-446a-946b-5752c2610b63}" linkData="{209450cc-2bad-438d-9918-b40d2ca20f3e}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{b4be4c22-404f-43c0-adc9-8fed46a06aff}" linkData="{a159d6c3-29fe-4da9-9345-a362bbbf4cb8}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{5d2450d5-1eb2-4dfb-9fe0-4daba5f7a2cd}" linkData="{a5a129b6-564f-46cb-8309-cf05419aa7ed}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{fe2deda2-992a-4e97-af19-f9d59dbc4ded}" linkData="{57488f4d-0076-4e58-94c5-9ff29d94a77f}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{9b365770-82c8-443c-af3d-2052b5f5676a}" linkData="{3875e962-9c33-4214-9049-34a042e653ad}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{f63c9190-de38-4e5b-8d31-93e5740eb2f2}" linkData="{2c7dcbf9-1455-49f4-9a7c-7ddb17c2179d}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{221aa226-dae0-41a1-8bc4-0d68a513b8d4}" linkData="{5a4379c7-bcc8-4e50-9c3c-24390279a7b1}" linkDataType="partId"/>
<component combineMode="Normal" expanded="false" id="{e49264f4-179b-42ec-9b3d-b022e1ab6ea2}" linkData="{0ec19fd0-d226-4ba3-8cda-0c444a398038}" linkDataType="partId"/>
</components>
<materials/>
<poses/>

View File

@ -0,0 +1,135 @@
#include <QDebug>
#include "centripetalcatmullromspline.h"
CentripetalCatmullRomSpline::CentripetalCatmullRomSpline(bool isClosed) :
m_isClosed(isClosed)
{
}
const std::vector<CentripetalCatmullRomSpline::SplineNode> &CentripetalCatmullRomSpline::splineNodes()
{
return m_splineNodes;
}
void CentripetalCatmullRomSpline::addPoint(int source, const QVector3D &position, bool isKnot)
{
if (isKnot)
m_splineKnots.push_back(m_splineNodes.size());
m_splineNodes.push_back({
source, position
});
}
float CentripetalCatmullRomSpline::atKnot(float t, const QVector3D &p0, const QVector3D &p1)
{
const float alpha = 0.5f;
QVector3D d = p1 - p0;
float a = QVector3D::dotProduct(d, d);
float b = std::pow(a, alpha * 0.5f);
return (b + t);
}
void CentripetalCatmullRomSpline::interpolateSegment(std::vector<QVector3D> &knots,
size_t from, size_t to)
{
const QVector3D &p0 = knots[0];
const QVector3D &p1 = knots[1];
const QVector3D &p2 = knots[2];
const QVector3D &p3 = knots[3];
float s1 = (p2 - p1).length();
float t0 = 0.0f;
float t1 = atKnot(t0, p0, p1);
float t2 = atKnot(t1, p1, p2);
float t3 = atKnot(t2, p2, p3);
for (size_t index = (from + 1) % m_splineNodes.size(); index != to; index = (index + 1) % m_splineNodes.size()) {
auto &position = m_splineNodes[index].position;
float factor = (position - p1).length() / s1;
float t = t1 * (1.0f - factor) + t2 * factor;
QVector3D a1 = (t1 - t) / (t1 - t0) * p0 + (t - t0) / (t1 - t0) * p1;
QVector3D a2 = (t2 - t) / (t2 - t1) * p1 + (t - t1) / (t2 - t1) * p2;
QVector3D a3 = (t3 - t) / (t3 - t2) * p2 + (t - t2) / (t3 - t2) * p3;
QVector3D b1 = (t2 - t) / (t2 - t0) * a1 + (t - t0) / (t2 - t0) * a2;
QVector3D b2 = (t3 - t) / (t3 - t1) * a2 + (t - t1) / (t3 - t1) * a3;
position = ((t2 - t) / (t2 - t1) * b1 + (t - t1) / (t2 - t1) * b2);
}
}
bool CentripetalCatmullRomSpline::interpolate()
{
if (m_isClosed)
return interpolateClosed();
return interpolateOpened();
}
bool CentripetalCatmullRomSpline::interpolateOpened()
{
if (m_splineKnots.size() < 3)
return false;
{
std::vector<QVector3D> knots = {
(m_splineNodes[m_splineKnots[0]].position + (m_splineNodes[m_splineKnots[0]].position - m_splineNodes[m_splineKnots[1]].position)),
m_splineNodes[m_splineKnots[0]].position,
m_splineNodes[m_splineKnots[1]].position,
m_splineNodes[m_splineKnots[2]].position
};
interpolateSegment(knots, m_splineKnots[0], m_splineKnots[1]);
}
{
size_t tail = m_splineKnots.size() - 1;
std::vector<QVector3D> knots = {
m_splineNodes[m_splineKnots[tail - 2]].position,
m_splineNodes[m_splineKnots[tail - 1]].position,
m_splineNodes[m_splineKnots[tail]].position,
(m_splineNodes[m_splineKnots[tail]].position + (m_splineNodes[m_splineKnots[tail]].position - m_splineNodes[m_splineKnots[tail - 1]].position))
};
interpolateSegment(knots, m_splineKnots[tail - 1], m_splineKnots[tail]);
}
for (size_t i = 1; i + 2 < m_splineKnots.size(); ++i) {
size_t h = i - 1;
size_t j = i + 1;
size_t k = i + 2;
std::vector<QVector3D> knots = {
m_splineNodes[m_splineKnots[h]].position,
m_splineNodes[m_splineKnots[i]].position,
m_splineNodes[m_splineKnots[j]].position,
m_splineNodes[m_splineKnots[k]].position
};
interpolateSegment(knots, m_splineKnots[i], m_splineKnots[j]);
}
return true;
}
bool CentripetalCatmullRomSpline::interpolateClosed()
{
if (m_splineKnots.size() < 3)
return false;
for (size_t h = 0; h < m_splineKnots.size(); ++h) {
size_t i = (h + 1) % m_splineKnots.size();
size_t j = (h + 2) % m_splineKnots.size();
size_t k = (h + 3) % m_splineKnots.size();
std::vector<QVector3D> knots = {
m_splineNodes[m_splineKnots[h]].position,
m_splineNodes[m_splineKnots[i]].position,
m_splineNodes[m_splineKnots[j]].position,
m_splineNodes[m_splineKnots[k]].position
};
interpolateSegment(knots, m_splineKnots[i], m_splineKnots[j]);
}
return true;
}

View File

@ -0,0 +1,31 @@
#ifndef DUST3D_CENTRIPETAL_CATMULL_ROM_SPLINE_H
#define DUST3D_CENTRIPETAL_CATMULL_ROM_SPLINE_H
#include <vector>
#include <QVector3D>
class CentripetalCatmullRomSpline
{
public:
struct SplineNode
{
int source = -1;
QVector3D position;
};
CentripetalCatmullRomSpline(bool isClosed);
void addPoint(int source, const QVector3D &position, bool isKnot);
bool interpolate();
const std::vector<SplineNode> &splineNodes();
private:
std::vector<SplineNode> m_splineNodes;
std::vector<size_t> m_splineKnots;
bool m_isClosed = false;
bool interpolateClosed();
bool interpolateOpened();
float atKnot(float t, const QVector3D &p0, const QVector3D &p1);
void interpolateSegment(std::vector<QVector3D> &knots,
size_t from, size_t to);
};
#endif

View File

@ -1197,6 +1197,8 @@ void Document::toSnapshot(Snapshot *snapshot, const std::set<QUuid> &limitNodeId
part["materialId"] = partIt.second.materialId.toString();
if (partIt.second.countershaded)
part["countershaded"] = "true";
if (partIt.second.smooth)
part["smooth"] = "true";
//if (partIt.second.gridded)
// part["gridded"] = "true";
snapshot->parts[part["id"]] = part;
@ -1629,6 +1631,7 @@ void Document::addFromSnapshot(const Snapshot &snapshot, enum SnapshotSource sou
if (materialIdIt != partKv.second.end())
part.materialId = oldNewIdMap[QUuid(materialIdIt->second)];
part.countershaded = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "countershaded"));
part.smooth = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "smooth"));
//part.gridded = isTrueValueString(valueOfKeyInMapOrEmpty(partKv.second, "gridded"));;
newAddedPartIds.insert(part.id);
}
@ -3166,6 +3169,21 @@ void Document::setPartCountershaded(QUuid partId, bool countershaded)
emit textureChanged();
}
void Document::setPartSmoothState(QUuid partId, bool smooth)
{
auto part = partMap.find(partId);
if (part == partMap.end()) {
qDebug() << "Part not found:" << partId;
return;
}
if (part->second.smooth == smooth)
return;
part->second.smooth = smooth;
part->second.dirty = true;
emit partSmoothStateChanged(partId);
emit skeletonChanged();
}
void Document::setPartCutRotation(QUuid partId, float cutRotation)
{
auto part = partMap.find(partId);

View File

@ -474,6 +474,7 @@ signals:
void partRoughnessChanged(QUuid partId);
void partHollowThicknessChanged(QUuid partId);
void partCountershadeStateChanged(QUuid partId);
void partSmoothStateChanged(QUuid partId);
void partGridStateChanged(QUuid partId);
void componentCombineModeChanged(QUuid componentId);
void cleanup();
@ -683,6 +684,7 @@ public slots:
void setPartRoughness(QUuid partId, float roughness);
void setPartHollowThickness(QUuid partId, float hollowThickness);
void setPartCountershaded(QUuid partId, bool countershaded);
void setPartSmoothState(QUuid partId, bool smooth);
void setComponentCombineMode(QUuid componentId, CombineMode combineMode);
void moveComponentUp(QUuid componentId);
void moveComponentDown(QUuid componentId);

View File

@ -1219,6 +1219,7 @@ DocumentWindow::DocumentWindow() :
connect(m_document, &Document::partMetalnessChanged, m_partTreeWidget, &PartTreeWidget::partMetalnessChanged);
connect(m_document, &Document::partRoughnessChanged, m_partTreeWidget, &PartTreeWidget::partRoughnessChanged);
connect(m_document, &Document::partCountershadeStateChanged, m_partTreeWidget, &PartTreeWidget::partCountershadeStateChanged);
connect(m_document, &Document::partSmoothStateChanged, m_partTreeWidget, &PartTreeWidget::partSmoothStateChanged);
connect(m_document, &Document::partTargetChanged, m_partTreeWidget, &PartTreeWidget::partXmirrorStateChanged);
connect(m_document, &Document::partTargetChanged, m_partTreeWidget, &PartTreeWidget::partColorStateChanged);

View File

@ -352,6 +352,7 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
bool rounded = isTrueValueString(valueOfKeyInMapOrEmpty(part, "rounded"));
bool chamfered = isTrueValueString(valueOfKeyInMapOrEmpty(part, "chamfered"));
bool countershaded = isTrueValueString(valueOfKeyInMapOrEmpty(part, "countershaded"));
bool smooth = isTrueValueString(valueOfKeyInMapOrEmpty(part, "smooth"));
QString colorString = valueOfKeyInMapOrEmpty(part, "color");
QColor partColor = colorString.isEmpty() ? m_defaultPartColor : QColor(colorString);
float deformThickness = 1.0;
@ -584,6 +585,8 @@ MeshCombiner::Mesh *MeshGenerator::combinePartMesh(const QString &partIdString,
strokeModifier = new StrokeModifier;
if (smooth)
strokeModifier->enableSmooth();
if (addIntermediateNodes)
strokeModifier->enableIntermediateAddition();

View File

@ -13,7 +13,6 @@
#include "model.h"
#include "componentlayer.h"
#include "clothforce.h"
#include "strokemodifier.h"
class GeneratedPart
{

View File

@ -1475,6 +1475,17 @@ void PartTreeWidget::partCountershadeStateChanged(QUuid partId)
widget->updateColorButton();
}
void PartTreeWidget::partSmoothStateChanged(QUuid partId)
{
auto item = m_partItemMap.find(partId);
if (item == m_partItemMap.end()) {
qDebug() << "Part item not found:" << partId;
return;
}
PartWidget *widget = (PartWidget *)itemWidget(item->second, 0);
widget->updateSmoothButton();
}
void PartTreeWidget::partChecked(QUuid partId)
{
auto item = m_partItemMap.find(partId);

View File

@ -82,6 +82,7 @@ public slots:
void partMetalnessChanged(QUuid partId);
void partRoughnessChanged(QUuid partId);
void partCountershadeStateChanged(QUuid partId);
void partSmoothStateChanged(QUuid partId);
void partChecked(QUuid partId);
void partUnchecked(QUuid partId);
void partComponentChecked(QUuid partId);

View File

@ -38,6 +38,11 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
m_visibleButton->setSizePolicy(retainSizePolicy);
initButton(m_visibleButton);
m_smoothButton = new QPushButton();
m_smoothButton->setToolTip(tr("Toggle smooth"));
m_smoothButton->setSizePolicy(retainSizePolicy);
initButton(m_smoothButton);
m_lockButton = new QPushButton();
m_lockButton->setToolTip(tr("Lock/unlock nodes"));
m_lockButton->setSizePolicy(retainSizePolicy);
@ -109,7 +114,7 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
toolsLayout->setContentsMargins(0, 0, 5, 0);
int row = 0;
int col = 0;
toolsLayout->addWidget(m_visibleButton, row, col++, Qt::AlignBottom);
toolsLayout->addWidget(m_smoothButton, row, col++, Qt::AlignBottom);
toolsLayout->addWidget(m_lockButton, row, col++, Qt::AlignBottom);
toolsLayout->addWidget(m_disableButton, row, col++, Qt::AlignBottom);
toolsLayout->addWidget(m_xMirrorButton, row, col++, Qt::AlignBottom);
@ -128,7 +133,7 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
QHBoxLayout *previewAndToolsLayout = new QHBoxLayout;
previewAndToolsLayout->setSpacing(0);
previewAndToolsLayout->setContentsMargins(0, 0, 0, 0);
//previewAndToolsLayout->addWidget(m_visibleButton);
previewAndToolsLayout->addWidget(m_visibleButton);
//previewAndToolsLayout->addWidget(m_previewWidget);
previewAndToolsLayout->addWidget(m_previewLabel);
previewAndToolsLayout->addLayout(toolsLayout);
@ -182,12 +187,23 @@ PartWidget::PartWidget(const Document *document, QUuid partId) :
connect(this, &PartWidget::setPartRoughness, m_document, &Document::setPartRoughness);
connect(this, &PartWidget::setPartHollowThickness, m_document, &Document::setPartHollowThickness);
connect(this, &PartWidget::setPartCountershaded, m_document, &Document::setPartCountershaded);
connect(this, &PartWidget::setPartSmoothState, m_document, &Document::setPartSmoothState);
connect(this, &PartWidget::checkPart, m_document, &Document::checkPart);
connect(this, &PartWidget::enableBackgroundBlur, m_document, &Document::enableBackgroundBlur);
connect(this, &PartWidget::disableBackgroundBlur, m_document, &Document::disableBackgroundBlur);
connect(this, &PartWidget::groupOperationAdded, m_document, &Document::saveSnapshot);
connect(m_smoothButton, &QPushButton::clicked, [=]() {
const SkeletonPart *part = m_document->findPart(m_partId);
if (!part) {
qDebug() << "Part not found:" << m_partId;
return;
}
emit setPartSmoothState(m_partId, !part->smooth);
emit groupOperationAdded();
});
connect(m_lockButton, &QPushButton::clicked, [=]() {
const SkeletonPart *part = m_document->findPart(m_partId);
if (!part) {
@ -313,12 +329,13 @@ ModelWidget *PartWidget::previewWidget()
QSize PartWidget::preferredSize()
{
return QSize(Theme::miniIconSize + Theme::partPreviewImageSize + Theme::miniIconSize * 4 + 5 + 2, Theme::partPreviewImageSize + 6);
return QSize(Theme::miniIconSize + Theme::partPreviewImageSize + Theme::miniIconSize * 5 + 5 + 2, Theme::partPreviewImageSize + 6);
}
void PartWidget::updateAllButtons()
{
updateVisibleButton();
updateSmoothButton();
updateLockButton();
updateSubdivButton();
updateDisableButton();
@ -828,6 +845,19 @@ void PartWidget::updateLockButton()
updateButton(m_lockButton, QChar(fa::unlock), false);
}
void PartWidget::updateSmoothButton()
{
const SkeletonPart *part = m_document->findPart(m_partId);
if (!part) {
qDebug() << "Part not found:" << m_partId;
return;
}
if (part->smooth)
updateButton(m_smoothButton, QChar(fa::headphones), true);
else
updateButton(m_smoothButton, QChar(fa::headphones), false);
}
void PartWidget::updateVisibleButton()
{
const SkeletonPart *part = m_document->findPart(m_partId);

View File

@ -33,6 +33,7 @@ signals:
void setPartRoughness(QUuid partId, float roughness);
void setPartHollowThickness(QUuid partId, float hollowThickness);
void setPartCountershaded(QUuid partId, bool countershaded);
void setPartSmoothState(QUuid partId, bool smooth);
void movePartUp(QUuid partId);
void movePartDown(QUuid partId);
void movePartToTop(QUuid partId);
@ -46,6 +47,7 @@ public:
void reload();
void updatePreview();
void updateLockButton();
void updateSmoothButton();
void updateVisibleButton();
void updateSubdivButton();
void updateDisableButton();
@ -73,6 +75,7 @@ private: // need initialize
private:
ModelWidget *m_previewWidget = nullptr;
QPushButton *m_visibleButton = nullptr;
QPushButton *m_smoothButton = nullptr;
QPushButton *m_lockButton = nullptr;
QPushButton *m_subdivButton = nullptr;
QPushButton *m_disableButton = nullptr;

View File

@ -188,6 +188,7 @@ public:
QUuid fillMeshLinkedId;
bool isPreviewMeshObsolete;
QPixmap previewPixmap;
bool smooth;
SkeletonPart(const QUuid &withId=QUuid()) :
visible(true),
locked(false),
@ -214,7 +215,8 @@ public:
hollowThickness(0.0),
countershaded(false),
gridded(false),
isPreviewMeshObsolete(false)
isPreviewMeshObsolete(false),
smooth(true)
{
id = withId.isNull() ? QUuid::createUuid() : withId;
}
@ -396,6 +398,10 @@ public:
target = other.target;
colorSolubility = other.colorSolubility;
countershaded = other.countershaded;
metalness = other.metalness;
roughness = other.roughness;
deformUnified = other.deformUnified;
smooth = other.smooth;
}
void updatePreviewMesh(Model *previewMesh)
{

View File

@ -1,13 +1,20 @@
#include <QVector2D>
#include <QDebug>
#include <unordered_map>
#include "strokemodifier.h"
#include "util.h"
#include "centripetalcatmullromspline.h"
void StrokeModifier::enableIntermediateAddition()
{
m_intermediateAdditionEnabled = true;
}
void StrokeModifier::enableSmooth()
{
m_smooth = true;
}
size_t StrokeModifier::addNode(const QVector3D &position, float radius, const std::vector<QVector2D> &cutTemplate, float cutRotation)
{
size_t nodeIndex = m_nodes.size();
@ -177,6 +184,187 @@ void StrokeModifier::finalize()
addEdge(nodeIndices[i - 1], nodeIndices[i]);
}
}
if (m_smooth)
smooth();
}
void StrokeModifier::smooth()
{
std::unordered_map<int, std::vector<int>> neighborMap;
for (const auto &edge: m_edges) {
neighborMap[edge.firstNodeIndex].push_back(edge.secondNodeIndex);
neighborMap[edge.secondNodeIndex].push_back(edge.firstNodeIndex);
}
int startEndpoint = 0;
for (const auto &edge: m_edges) {
auto findNeighbor = neighborMap.find(edge.firstNodeIndex);
if (findNeighbor == neighborMap.end())
continue;
if (1 != findNeighbor->second.size()) {
auto findNeighborNeighbor = neighborMap.find(edge.secondNodeIndex);
if (findNeighborNeighbor == neighborMap.end())
continue;
if (1 != findNeighborNeighbor->second.size())
continue;
startEndpoint = edge.secondNodeIndex;
} else {
startEndpoint = edge.firstNodeIndex;
}
break;
}
if (-1 == startEndpoint)
return;
int loopIndex = startEndpoint;
int previousIndex = -1;
bool isRing = false;
std::vector<int> loop;
while (-1 != loopIndex) {
loop.push_back(loopIndex);
auto findNeighbor = neighborMap.find(loopIndex);
if (findNeighbor == neighborMap.end())
return;
int nextIndex = -1;
for (const auto &index: findNeighbor->second) {
if (index == previousIndex)
continue;
if (index == startEndpoint) {
isRing = true;
break;
}
nextIndex = index;
break;
}
previousIndex = loopIndex;
loopIndex = nextIndex;
}
CentripetalCatmullRomSpline spline(isRing);
for (size_t i = 0; i < loop.size(); ++i) {
const auto &nodeIndex = loop[i];
const auto &node = m_nodes[nodeIndex];
bool isKnot = node.originNodeIndex == nodeIndex;
qDebug() << "Loop[" << i << "]: isKnot:" << (isKnot ? "TRUE" : "false") << "nodeIndex:" << nodeIndex;
spline.addPoint((int)nodeIndex, node.position, isKnot);
}
if (!spline.interpolate())
return;
for (const auto &it: spline.splineNodes()) {
if (-1 == it.source)
continue;
auto &node = m_nodes[it.source];
node.position = it.position;
}
/*
auto atKnot = [](float t, const QVector3D &p0, const QVector3D &p1) {
const float alpha = 0.5f;
QVector3D d = p1 - p0;
float a = QVector3D::dotProduct(d, d);
float b = std::pow(a, alpha * 0.5f);
return (b + t);
};
auto centripetalCatmullRom = [&](std::vector<int> &knots,
size_t segment, const std::vector<float> &times, const std::vector<size_t> &nodeIndices) {
const QVector3D &p0 = m_nodes[knots[0]].position;
const QVector3D &p1 = m_nodes[knots[1]].position;
const QVector3D &p2 = m_nodes[knots[2]].position;
const QVector3D &p3 = m_nodes[knots[3]].position;
float t0 = 0.0f;
float t1 = atKnot(t0, p0, p1);
float t2 = atKnot(t1, p1, p2);
float t3 = atKnot(t2, p2, p3);
qDebug() << "t0:" << t0;
qDebug() << "t1:" << t1;
qDebug() << "t2:" << t2;
qDebug() << "t3:" << t3;
qDebug() << "p0:" << p0;
qDebug() << "p1:" << p1;
qDebug() << "p2:" << p2;
qDebug() << "p3:" << p3;
for (size_t i = 0; i < times.size(); ++i) {
const auto &factor = times[i];
float t = 0.0;
if (0 == segment)
t = t0 * (1.0f - factor) + t1 * factor;
else if (1 == segment)
t = t1 * (1.0f - factor) + t2 * factor;
else
t = t2 * (1.0f - factor) + t3 * factor;
QVector3D a1 = (t1 - t) / (t1 - t0) * p0 + (t - t0) / (t1 - t0) * p1;
QVector3D a2 = (t2 - t) / (t2 - t1) * p1 + (t - t1) / (t2 - t1) * p2;
QVector3D a3 = (t3 - t) / (t3 - t2) * p2 + (t - t2) / (t3 - t2) * p3;
QVector3D b1 = (t2 - t) / (t2 - t0) * a1 + (t - t0) / (t2 - t0) * a2;
QVector3D b2 = (t3 - t) / (t3 - t1) * a2 + (t - t1) / (t3 - t1) * a3;
m_nodes[nodeIndices[i]].position = ((t2 - t) / (t2 - t1) * b1 + (t - t1) / (t2 - t1) * b2);
qDebug() << "Update node position:" << m_nodes[nodeIndices[i]].position << "factor:" << factor << "t:" << t;
}
};
struct SplineNode
{
size_t order;
int nodeIndex;
float distance;
};
std::vector<SplineNode> splineNodes;
std::vector<size_t> splineKnots;
float distance = 0;
for (size_t i = 0; i < loop.size(); ++i) {
const auto &nodeIndex = loop[i];
const auto &node = m_nodes[nodeIndex];
if (i > 0)
distance += (m_nodes[loop[i - 1]].position - node.position).length();
if (node.originNodeIndex == nodeIndex)
splineKnots.push_back(splineNodes.size());
splineNodes.push_back({
i, nodeIndex, distance
});
}
qDebug() << "splineNodes.size():" << splineNodes.size();
qDebug() << "splineKnots.size():" << splineKnots.size();
for (size_t i = 0; i < splineKnots.size(); ++i) {
size_t h = i > 0 ? i - 1 : i;
size_t j = i + 1 < splineKnots.size() ? i + 1 : i;
size_t k = i + 2 < splineKnots.size() ? i + 2 : j;
qDebug() << "h:" << h;
qDebug() << "i:" << i;
qDebug() << "j:" << j;
qDebug() << "k:" << k;
if (h == i || i == j || j == k)
continue;
float beginDistance = splineNodes[splineKnots[i]].distance;
float endDistance = splineNodes[splineKnots[j]].distance;
float totalDistance = endDistance - beginDistance;
std::vector<float> times;
std::vector<size_t> nodeIndices;
for (size_t splineNodeIndex = splineKnots[i] + 1;
splineNodeIndex < splineKnots[j]; ++splineNodeIndex) {
qDebug() << "splineNodeIndex:" << splineNodeIndex;
times.push_back((splineNodes[splineNodeIndex].distance - beginDistance) / totalDistance);
nodeIndices.push_back(splineNodes[splineNodeIndex].nodeIndex);
}
std::vector<int> knots = {
splineNodes[splineKnots[h]].nodeIndex,
splineNodes[splineKnots[i]].nodeIndex,
splineNodes[splineKnots[j]].nodeIndex,
splineNodes[splineKnots[k]].nodeIndex
};
centripetalCatmullRom(knots, 1, times, nodeIndices);
}
*/
}
const std::vector<StrokeModifier::Node> &StrokeModifier::nodes() const

View File

@ -30,6 +30,7 @@ public:
void subdivide();
void roundEnd();
void enableIntermediateAddition();
void enableSmooth();
const std::vector<Node> &nodes() const;
const std::vector<Edge> &edges() const;
void finalize();
@ -39,10 +40,12 @@ private:
std::vector<Node> m_nodes;
std::vector<Edge> m_edges;
bool m_intermediateAdditionEnabled = false;
bool m_smooth = false;
void createIntermediateNode(const Node &firstNode, const Node &secondNode, float factor, Node *resultNode);
float averageCutTemplateEdgeLength(const std::vector<QVector2D> &cutTemplate);
void createIntermediateCutTemplateEdges(std::vector<QVector2D> &cutTemplate, float averageCutTemplateLength);
void smooth();
};
#endif