forked from 1b-agent/opencadstudio-upstream
feat(offset): open on the live preview for pre-selected objects; signed typed distance
With objects already selected, OFFSET opens at the side step with the preview following the cursor instead of asking for a distance first (#422). At the side step a typed distance places the offset at once on the cursor's side, and a negative one on the other side; Enter at the start repeats the last distance (#418). Erase and Layer, which lived on the skipped distance step, are offered there too. With nothing selected the distance-first flow is unchanged. The opposite side is found by mirroring the cursor through its nearest point on the outline, capped at half the offset distance so it cannot cross a narrow gap back to the cursor's own side. Two new prompts are added to the catalog and every locale. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
3f54f2f494
commit
51abbd897c
27 files changed
+365
-109
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 167 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 166 KiB |
@@ -3035,6 +3035,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ انقر نقطة العبور أو [متعدد]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ متعدد انقر الجانب التالي [المسافة __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ متعدد انقر نقطة العبور التالية:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ انقر نقطة العبور أو اكتب المسافة، والقيمة السالبة للجهة الأخرى [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ متعدد انقر نقطة العبور أو اكتب المسافة، والقيمة السالبة للجهة الأخرى [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = إزاحة (DIMEXO)
|
||||
.overall-scale-dimscale = المقياس الإجمالي (DIMSCALE)
|
||||
.pedit-enter-option = PEDIT أدخل الخيار:
|
||||
|
||||
@@ -3022,6 +3022,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ Кликни точка на преминаване или [Многократно]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ Многократно Кликни следваща страна [разстояние __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ Многократно Кликни следваща точка на преминаване:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Кликни точка на преминаване или въведи разстояние, отрицателно за другата страна [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Многократно Кликни точка на преминаване или въведи разстояние, отрицателно за другата страна [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = Отместване (DIMEXO)
|
||||
.overall-scale-dimscale = Общ мащаб (DIMSCALE)
|
||||
.pedit-enter-option = PEDIT Въведи опция:
|
||||
|
||||
@@ -3022,6 +3022,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ Bod prokliku nebo [Multiple]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ Vícenásobné kliknutí na další stranu [vzdálenost __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ Vícenásobné kliknutí dalším průchozím bodem:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Klikněte na průchozí bod nebo zadejte vzdálenost, zápornou pro druhou stranu [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Vícenásobně Klikněte na průchozí bod nebo zadejte vzdálenost, zápornou pro druhou stranu [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = Odsazení (DIMEXO)
|
||||
.overall-scale-dimscale = Celková stupnice (DIMSCALE)
|
||||
.pedit-enter-option = Možnost PEDIT Enter:
|
||||
|
||||
@@ -3023,6 +3023,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ Klicken Sie durch Punkt oder [Multiple]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ Mehrfachklicken Sie auf die nächste Seite [Abstand __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ Mehrfachklicken Sie als nächstes durch den Punkt:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Durch Punkt klicken oder Abstand eingeben, negativ für die andere Seite [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Mehrfach Durch Punkt klicken oder Abstand eingeben, negativ für die andere Seite [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = Abstand der Maßhilfslinie (DIMEXO)
|
||||
.overall-scale-dimscale = Gesamtmaßstab (DIMSCALE)
|
||||
.pedit-enter-option = PEDIT Option eingeben:
|
||||
|
||||
@@ -3023,6 +3023,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ Κάντε κλικ σε σημείο διέλευσης ή [Πολλαπλά]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ Πολλαπλά Κάντε κλικ στην επόμενη πλευρά [απόσταση __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ Πολλαπλά Κάντε κλικ στο επόμενο σημείο διέλευσης:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Κάντε κλικ σε σημείο διέλευσης ή πληκτρολογήστε απόσταση, αρνητική για την άλλη πλευρά [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Πολλαπλά Κάντε κλικ σε σημείο διέλευσης ή πληκτρολογήστε απόσταση, αρνητική για την άλλη πλευρά [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = Μετατόπιση (DIMEXO)
|
||||
.overall-scale-dimscale = Συνολική κλίμακα (DIMSCALE)
|
||||
.pedit-enter-option = PEDIT Δώστε επιλογή:
|
||||
|
||||
@@ -3024,6 +3024,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ Click through point or [Multiple]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ Multiple Click next side [distance __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ Multiple Click next through point:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Click through point or type distance, negative for the other side [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Multiple Click through point or type distance, negative for the other side [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = Offset (DIMEXO)
|
||||
.overall-scale-dimscale = Overall scale (DIMSCALE)
|
||||
.pedit-enter-option = PEDIT Enter option:
|
||||
|
||||
@@ -3023,6 +3023,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ Pulse el punto de paso o [Múltiple]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ Múltiple Pulse el siguiente lado [distancia __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ Múltiple Pulse el siguiente punto de paso:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Pulse el punto de paso o escriba la distancia, negativa para el otro lado [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Múltiple Pulse el punto de paso o escriba la distancia, negativa para el otro lado [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = Desfase (DIMEXO)
|
||||
.overall-scale-dimscale = Escala general (DIMSCALE)
|
||||
.pedit-enter-option = PEDIT Indique una opción:
|
||||
|
||||
@@ -3022,6 +3022,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ Napsautuskohta tai [Multiple]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ Useita Napsauta seuraavaa reunaa [etäisyys __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ Useita napsauttamalla seuraava kohta:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Napsauta läpikulkupistettä tai kirjoita etäisyys, negatiivinen toiselle puolelle [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Useita Napsauta läpikulkupistettä tai kirjoita etäisyys, negatiivinen toiselle puolelle [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = Siirtymä (DIMEXO)
|
||||
.overall-scale-dimscale = Kokonaismittakaava (DIMSCALE)
|
||||
.pedit-enter-option = PEDIT Enter vaihtoehto:
|
||||
|
||||
@@ -3023,6 +3023,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ Cliquez sur le point ou [multiple]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ Cliquer sur le côté suivant [distance __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ Cliquez sur plusieurs ci-dessous pour accéder au point suivant:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Cliquez sur le point de passage ou saisissez la distance, négative pour l'autre côté [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Multiple Cliquez sur le point de passage ou saisissez la distance, négative pour l'autre côté [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = Décomposition (DIMEXO)
|
||||
.overall-scale-dimscale = Échelle globale (DIMSCALE)
|
||||
.pedit-enter-option = PEDIT Saisissez l'option :
|
||||
|
||||
@@ -3011,6 +3011,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ बिंदु के माध्यम से क्लिक करें या [एकाधिक]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ अगली दिशा की ओर क्लिक करें [दूरी __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ एकाधिक बिंदु के माध्यम से आगे क्लिक करें:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ पारगमन बिंदु पर क्लिक करें या दूरी टाइप करें, दूसरी ओर के लिए ऋणात्मक [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ एकाधिक पारगमन बिंदु पर क्लिक करें या दूरी टाइप करें, दूसरी ओर के लिए ऋणात्मक [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = ऑफसेट (DIMEXO)
|
||||
.overall-scale-dimscale = कुल मिलाकर स्केल (DIMSCALE)
|
||||
.pedit-enter-option = PEDIT विकल्प दर्ज करें:
|
||||
|
||||
@@ -3022,6 +3022,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ Átkattintási pont vagy [Multiple]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ Több kattintás a következő oldalon [távolság __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ Több kattintás a következő ponton:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Kattintson az átmenő pontra, vagy írja be a távolságot, negatívat a másik oldalhoz [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Többszörös Kattintson az átmenő pontra, vagy írja be a távolságot, negatívat a másik oldalhoz [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = Eltolás (DIMEXO)
|
||||
.overall-scale-dimscale = Teljes skála (DIMSCALE)
|
||||
.pedit-enter-option = PEDIT Enter opció:
|
||||
|
||||
@@ -3022,6 +3022,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ Punto di passaggio o [Multiple]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ Clic multiplo sul lato successivo [distanza __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ Clic multiplo sul punto successivo:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Fare clic sul punto di passaggio o digitare la distanza, negativa per l'altro lato [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Multiplo Fare clic sul punto di passaggio o digitare la distanza, negativa per l'altro lato [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = Spostamento (DIMEXO)
|
||||
.overall-scale-dimscale = Scala totale (DIMSCALE)
|
||||
.pedit-enter-option = Opzione PEDIT Enter:
|
||||
|
||||
@@ -3013,6 +3013,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ 通過点をクリックまたは [複数]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ 複数 次の側をクリック [距離 __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ 複数 次の通過点をクリック:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ 通過点をクリックまたは距離を入力、負の値で反対側 [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ 複数 通過点をクリックまたは距離を入力、負の値で反対側 [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = オフセット (DIMEXO)
|
||||
.overall-scale-dimscale = 全体尺度 (DIMSCALE)
|
||||
.pedit-enter-option = PEDIT オプションを入力:
|
||||
|
||||
@@ -3022,6 +3022,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ 클릭 통과 점 또는 [Multiple]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ 다중 다음 측면 클릭 [거리 __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ 다중 클릭 다음 통과 지점:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ 통과점을 클릭하거나 거리 입력, 음수는 반대쪽 [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ 다중 통과점을 클릭하거나 거리 입력, 음수는 반대쪽 [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = 오프셋(DIMEXO)
|
||||
.overall-scale-dimscale = 전체 규모(DIMSCALE)
|
||||
.pedit-enter-option = PEDIT Enter 옵션:
|
||||
|
||||
@@ -3023,6 +3023,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ Klik door punt of [meerdere]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ Meerdere klik op volgende zijde [afstand __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ Meerdere klik volgende door punt:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Klik door punt of typ afstand, negatief voor de andere kant [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Meerdere Klik door punt of typ afstand, negatief voor de andere kant [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = Verschuiving (DIMEXO)
|
||||
.overall-scale-dimscale = Totale schaal (DIMSCALE)
|
||||
.pedit-enter-option = PEDIT Voer optie in:
|
||||
|
||||
@@ -3022,6 +3022,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ Punkt przelotowy lub [Multiple]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ Wielokrotne kliknięcie następnej strony [odległość __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ Wielokrotne kliknięcie następnego punktu przelotowego:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Kliknij punkt przelotowy lub wpisz odległość, ujemną dla drugiej strony [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Wielokrotnie Kliknij punkt przelotowy lub wpisz odległość, ujemną dla drugiej strony [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = Przesunięcie (DIMEXO)
|
||||
.overall-scale-dimscale = Skala ogólna (DIMSCALE)
|
||||
.pedit-enter-option = Opcja PEDIT Enter:
|
||||
|
||||
@@ -3023,6 +3023,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ Clique no ponto de passagem ou [Múltiplo]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ Múltiplo Clique no próximo lado [distância __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ Múltiplo Clique no próximo ponto de passagem:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Clique no ponto de passagem ou digite a distância, negativa para o outro lado [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Múltiplo Clique no ponto de passagem ou digite a distância, negativa para o outro lado [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = Deslocamento (DIMEXO)
|
||||
.overall-scale-dimscale = Escala geral (DIMSCALE)
|
||||
.pedit-enter-option = PEDIT Insira a opção:
|
||||
|
||||
@@ -3026,6 +3026,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ Нажмите на точку или [Множественное число]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ Несколько кликов на следующую сторону [расстояние __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ Несколько кликов по следующей точке:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Укажите точку прохождения или введите расстояние, отрицательное для другой стороны [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Несколько Укажите точку прохождения или введите расстояние, отрицательное для другой стороны [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = Отступ от объекта (DIMEXO)
|
||||
.overall-scale-dimscale = Общий масштаб (DIMSCALE)
|
||||
.pedit-enter-option = PEDIT Введите опцию:
|
||||
|
||||
@@ -3005,6 +3005,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ Geçiş noktasına tıklayın veya [Birden Çok]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ Birden Çok Sonraki tarafa tıklayın [mesafe __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ Birden Çok Sonraki geçiş noktasına tıklayın:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Geçiş noktasına tıklayın veya mesafe yazın, diğer taraf için negatif [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ Birden Çok Geçiş noktasına tıklayın veya mesafe yazın, diğer taraf için negatif [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = Ofset (DIMEXO)
|
||||
.overall-scale-dimscale = Genel ölçek (DIMSCALE)
|
||||
.pedit-enter-option = PEDIT Seçeneği girin:
|
||||
|
||||
@@ -3010,6 +3010,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ 单击通过点或 [多个]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ 多个 单击下一侧 [距离 __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ 多个 单击下一个通过点:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ 单击通过点或输入距离,负值偏移到另一侧 [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ 多个 单击通过点或输入距离,负值偏移到另一侧 [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = 偏移 (DIMEXO)
|
||||
.overall-scale-dimscale = 全局比例 (DIMSCALE)
|
||||
.pedit-enter-option = PEDIT 输入选项:
|
||||
|
||||
@@ -3022,6 +3022,8 @@ modify =
|
||||
.offset-n-click-through-point-or-multiple = OFFSET__ocs_arg_n__ 按一下通過點或 [多個]:
|
||||
.offset-n-multiple-click-next-side-distance-d = OFFSET__ocs_arg_n__ 多個 按一下下一側 [距離 __ocs_arg_d__]:
|
||||
.offset-n-multiple-click-next-through-point = OFFSET__ocs_arg_n__ 多個 按一下下一個通過點:
|
||||
.offset-n-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ 按一下通過點或輸入距離,負值偏移到另一側 [Multiple/Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-n-multiple-click-through-point-or-type-distance = OFFSET__ocs_arg_n__ 多個 按一下通過點或輸入距離,負值偏移到另一側 [Erase/Layer] <__ocs_arg_d__>:
|
||||
.offset-dimexo = 偏移 (DIMEXO)
|
||||
.overall-scale-dimscale = 全局比例 (DIMSCALE)
|
||||
.pedit-enter-option = PEDIT 輸入選項:
|
||||
|
||||
@@ -1268,7 +1268,8 @@ impl OpenCADStudio {
|
||||
})
|
||||
.collect();
|
||||
// Pick-first (#422): with offsettable objects already selected,
|
||||
// skip the pick step and go straight to distance / side.
|
||||
// skip the distance and pick steps and open on the live side
|
||||
// preview.
|
||||
let preselected: Vec<_> = self.tabs[i]
|
||||
.scene
|
||||
.selected_entities()
|
||||
|
||||
@@ -2049,6 +2049,8 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
|
||||
"OFFSET%{n} Click through point or [Multiple]:" => Some(("modify", "offset-n-click-through-point-or-multiple")),
|
||||
"OFFSET%{n} Multiple Click next side [distance %{d}]:" => Some(("modify", "offset-n-multiple-click-next-side-distance-d")),
|
||||
"OFFSET%{n} Multiple Click next through point:" => Some(("modify", "offset-n-multiple-click-next-through-point")),
|
||||
"OFFSET%{n} Click through point or type distance, negative for the other side [Multiple/Erase/Layer] <%{d}>:" => Some(("modify", "offset-n-click-through-point-or-type-distance")),
|
||||
"OFFSET%{n} Multiple Click through point or type distance, negative for the other side [Erase/Layer] <%{d}>:" => Some(("modify", "offset-n-multiple-click-through-point-or-type-distance")),
|
||||
"OK" => Some(("common", "ok")),
|
||||
"OLE Frame" => Some(("properties", "ole-frame")),
|
||||
"OOPS: nothing to restore." => Some(("file", "oops-nothing-to-restore")),
|
||||
|
||||
+319
-108
@@ -9,6 +9,10 @@
|
||||
// 2. Pick object to offset (Line, Arc, Circle, LwPolyline)
|
||||
// 3. Pick a point on the side to offset toward, or choose Multiple
|
||||
// to keep offsetting the newly created result at the same distance
|
||||
//
|
||||
// With objects already selected, the command opens at step 3 instead, the
|
||||
// preview following the cursor. At step 3 a typed distance places the
|
||||
// offset at once on the cursor's side; a negative one, on the other side.
|
||||
|
||||
use crate::entities::curve::{entity_curve, lwpolyline_world_xy};
|
||||
use crate::modules::draw::modify::spline_ops::spline_sample_xy;
|
||||
@@ -331,13 +335,18 @@ fn compute_offsets(entity: &EntityType, dist: f64, side_pt: Vec3) -> Vec<EntityT
|
||||
// distance for every supported entity type.
|
||||
|
||||
fn perp_distance(entity: &EntityType, pt: Vec3) -> f64 {
|
||||
closest_on_wire(entity, pt).map_or(0.0, |(_, d)| d)
|
||||
}
|
||||
|
||||
/// Nearest point of the entity outline to `pt`, and its distance.
|
||||
fn closest_on_wire(entity: &EntityType, pt: Vec3) -> Option<([f64; 2], f64)> {
|
||||
let pts = entity_wire_pts(entity);
|
||||
if pts.len() < 2 {
|
||||
return 0.0;
|
||||
return None;
|
||||
}
|
||||
let px = pt.x as f64;
|
||||
let py = pt.y as f64;
|
||||
let mut best = f64::INFINITY;
|
||||
let mut best = ([px, py], f64::INFINITY);
|
||||
for w in pts.windows(2) {
|
||||
let ax = w[0][0] as f64;
|
||||
let ay = w[0][1] as f64;
|
||||
@@ -354,11 +363,35 @@ fn perp_distance(entity: &EntityType, pt: Vec3) -> f64 {
|
||||
let cx = ax + t * dx;
|
||||
let cy = ay + t * dy;
|
||||
let d = ((px - cx).powi(2) + (py - cy).powi(2)).sqrt();
|
||||
if d < best {
|
||||
best = d;
|
||||
if d < best.1 {
|
||||
best = ([cx, cy], d);
|
||||
}
|
||||
}
|
||||
best
|
||||
Some(best)
|
||||
}
|
||||
|
||||
// ── Opposite side ─────────────────────────────────────────────────────────
|
||||
//
|
||||
// A negative typed distance offsets away from the cursor. Rather than teach
|
||||
// every offset function a sign, the cursor is mirrored through its nearest
|
||||
// point on the outline, which puts it on the other side for every type: the
|
||||
// other half-plane of a line, inside instead of outside a circle or closed
|
||||
// loop. The mirror stops at half the offset distance so that, in a narrow
|
||||
// part of a shape, it cannot cross the far wall and come back to the
|
||||
// cursor's own side.
|
||||
|
||||
fn far_side(entity: &EntityType, pt: Vec3, dist: f64) -> Option<Vec3> {
|
||||
let ([qx, qy], d) = closest_on_wire(entity, pt)?;
|
||||
if d < 1e-9 {
|
||||
// On the outline itself: there is no side to be opposite to.
|
||||
return None;
|
||||
}
|
||||
let step = d.min(dist * 0.5) / d;
|
||||
Some(Vec3::new(
|
||||
(qx + (qx - pt.x as f64) * step) as f32,
|
||||
(qy + (qy - pt.y as f64) * step) as f32,
|
||||
pt.z,
|
||||
))
|
||||
}
|
||||
|
||||
// ── Wire preview points ─────────────────────────────────────────────────────
|
||||
@@ -440,9 +473,13 @@ pub struct OffsetCommand {
|
||||
/// Unlike the command's opening snapshot, this also includes objects
|
||||
/// created by earlier offsets while the command remains active.
|
||||
picked: Option<EntityType>,
|
||||
/// Pre-selected offsettable objects (pick-first, #422); consumed when the
|
||||
/// distance step resolves.
|
||||
preselected: Vec<EntityType>,
|
||||
/// Last cursor position the preview saw. A distance typed at the side
|
||||
/// step places the offset relative to it.
|
||||
cursor: Option<DVec3>,
|
||||
/// The command opened on pre-selected objects (#422) and has made nothing
|
||||
/// yet: Enter takes the last distance, and Erase / Layer, which the
|
||||
/// skipped distance step would have offered, are offered here.
|
||||
drag_start: bool,
|
||||
/// A Yes/No or Current/Source answer is being collected for this option.
|
||||
awaiting: Option<Await>,
|
||||
/// Erase option: replace the source object with its offset.
|
||||
@@ -485,7 +522,8 @@ impl OffsetCommand {
|
||||
all_entities,
|
||||
entity_index,
|
||||
picked: None,
|
||||
preselected: Vec::new(),
|
||||
cursor: None,
|
||||
drag_start: false,
|
||||
awaiting: None,
|
||||
erase_source: false,
|
||||
layer_current: false,
|
||||
@@ -494,38 +532,28 @@ impl OffsetCommand {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pick-first flow (#422): the distance step still comes first, then the
|
||||
/// pre-selected objects go straight to the side step.
|
||||
/// Pick-first flow (#422): the objects are already chosen, so the command
|
||||
/// opens at the side step with the preview following the cursor. Clicking
|
||||
/// offsets through the cursor; typing a distance places it at that
|
||||
/// distance; Enter repeats the last distance (#418).
|
||||
pub fn with_selection(all_entities: Vec<EntityType>, targets: Vec<EntityType>) -> Self {
|
||||
let entity_index = ModifyEntityIndex::build(&all_entities);
|
||||
Self {
|
||||
step: Step::Distance,
|
||||
all_entities,
|
||||
entity_index,
|
||||
picked: None,
|
||||
preselected: targets,
|
||||
awaiting: None,
|
||||
erase_source: false,
|
||||
layer_current: false,
|
||||
targets_are_sources: false,
|
||||
made: 0,
|
||||
let mut cmd = Self::new(all_entities);
|
||||
if !targets.is_empty() {
|
||||
cmd.step = Step::PickSide {
|
||||
targets,
|
||||
locked: None,
|
||||
multiple: false,
|
||||
};
|
||||
cmd.targets_are_sources = true;
|
||||
cmd.drag_start = true;
|
||||
}
|
||||
cmd
|
||||
}
|
||||
|
||||
/// Leave the distance step with the given mode (Some = locked distance,
|
||||
/// None = through): pre-selected objects jump to the side step, otherwise
|
||||
/// the object-pick loop starts.
|
||||
/// None = through) for the object-pick loop.
|
||||
fn advance_from_distance(&mut self, locked: Option<f64>) -> CmdResult {
|
||||
if self.preselected.is_empty() {
|
||||
self.step = Step::SelectObject { locked };
|
||||
} else {
|
||||
self.step = Step::PickSide {
|
||||
targets: std::mem::take(&mut self.preselected),
|
||||
locked,
|
||||
multiple: false,
|
||||
};
|
||||
self.targets_are_sources = true;
|
||||
}
|
||||
self.step = Step::SelectObject { locked };
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
|
||||
@@ -554,6 +582,73 @@ impl OffsetCommand {
|
||||
t!("OFFSET distance = %{d}", d = d).into_owned()
|
||||
)
|
||||
}
|
||||
|
||||
/// Offset the side step's targets with `pt` choosing the side: toward it,
|
||||
/// or away from it when `flip` is set. Each target offsets by its own
|
||||
/// through-distance, or by the locked magnitude.
|
||||
fn place(&mut self, pt: DVec3, flip: bool) -> CmdResult {
|
||||
let (locked, targets, multiple) = match &self.step {
|
||||
Step::PickSide {
|
||||
locked,
|
||||
targets,
|
||||
multiple,
|
||||
} => (*locked, targets.clone(), *multiple),
|
||||
|
||||
_ => return CmdResult::NeedPoint,
|
||||
};
|
||||
let per_target: Vec<(Handle, Vec<EntityType>)> = targets
|
||||
.iter()
|
||||
.filter_map(|entity| {
|
||||
let mag = locked.unwrap_or_else(|| perp_distance(entity, pt.as_vec3()));
|
||||
if mag < 1e-9 {
|
||||
return None;
|
||||
}
|
||||
let side = if flip {
|
||||
far_side(entity, pt.as_vec3(), mag)?
|
||||
} else {
|
||||
pt.as_vec3()
|
||||
};
|
||||
let offsets = compute_offsets(entity, mag, side);
|
||||
(!offsets.is_empty()).then(|| (entity.common().handle, offsets))
|
||||
})
|
||||
.collect();
|
||||
let mut news: Vec<EntityType> = per_target
|
||||
.iter()
|
||||
.flat_map(|(_, offsets)| offsets.iter().cloned())
|
||||
.collect();
|
||||
if news.is_empty() {
|
||||
return CmdResult::NeedPoint;
|
||||
}
|
||||
self.made += 1;
|
||||
self.drag_start = false;
|
||||
// Erase option: the offset replaces its source object. Only the
|
||||
// objects picked from the drawing qualify — a Multiple chain's
|
||||
// targets are the offsets just made, which are not replaced again.
|
||||
let erase = self.erase_source && self.targets_are_sources;
|
||||
self.targets_are_sources = false;
|
||||
if multiple {
|
||||
// Multiple mode chains from the result just created, matching
|
||||
// OFFSET's repeated fixed-distance behavior (parallel/concentric
|
||||
// series instead of duplicate copies at the first offset).
|
||||
self.step = Step::PickSide {
|
||||
targets: news.clone(),
|
||||
locked,
|
||||
multiple: true,
|
||||
};
|
||||
} else {
|
||||
// Classic loop (#418): commit this offset and go back to the object
|
||||
// pick at the same distance, until Enter / Esc finishes.
|
||||
self.step = Step::SelectObject { locked };
|
||||
}
|
||||
if erase {
|
||||
return CmdResult::ReplaceManyContinue(per_target);
|
||||
}
|
||||
if news.len() == 1 {
|
||||
CmdResult::CommitEntity(news.pop().unwrap())
|
||||
} else {
|
||||
CmdResult::CommitEntities(news)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CadCommand for OffsetCommand {
|
||||
@@ -602,6 +697,23 @@ impl CadCommand for OffsetCommand {
|
||||
} else {
|
||||
std::borrow::Cow::Borrowed("")
|
||||
};
|
||||
if self.drag_start {
|
||||
let d = format!("{:.4}", defaults::get_offset_dist());
|
||||
return if *multiple {
|
||||
t!(
|
||||
"OFFSET%{n} Multiple Click through point or type distance, negative for the other side [Erase/Layer] <%{d}>:",
|
||||
n = n,
|
||||
d = d
|
||||
)
|
||||
} else {
|
||||
t!(
|
||||
"OFFSET%{n} Click through point or type distance, negative for the other side [Multiple/Erase/Layer] <%{d}>:",
|
||||
n = n,
|
||||
d = d
|
||||
)
|
||||
}
|
||||
.into_owned();
|
||||
}
|
||||
match (locked, multiple) {
|
||||
(Some(d), false) => {
|
||||
let d = format!("{:.4}", d);
|
||||
@@ -658,6 +770,16 @@ impl CadCommand for OffsetCommand {
|
||||
opts.push(CmdOption::enter("Exit"));
|
||||
opts
|
||||
}
|
||||
Step::PickSide { multiple, .. } if self.drag_start => {
|
||||
let mut opts = Vec::new();
|
||||
if !multiple {
|
||||
opts.push(CmdOption::new("Multiple", "M"));
|
||||
}
|
||||
opts.push(CmdOption::new("Erase", "E"));
|
||||
opts.push(CmdOption::new("Layer", "L"));
|
||||
opts.push(CmdOption::enter(&format!("{:.4}", defaults::get_offset_dist())));
|
||||
opts
|
||||
}
|
||||
Step::PickSide { multiple, .. } => {
|
||||
let mut opts = Vec::new();
|
||||
if !multiple {
|
||||
@@ -777,19 +899,23 @@ impl CadCommand for OffsetCommand {
|
||||
{
|
||||
return self.undo_last();
|
||||
}
|
||||
// Erase / Layer belong to the distance step, or to the side step when
|
||||
// a pre-selection skipped the distance step.
|
||||
if matches!(self.step, Step::Distance) || self.drag_start {
|
||||
if upper == "E" || upper == "ERASE" {
|
||||
self.awaiting = Some(Await::Erase);
|
||||
return Some(CmdResult::NeedPoint);
|
||||
}
|
||||
if upper == "L" || upper == "LAYER" {
|
||||
self.awaiting = Some(Await::Layer);
|
||||
return Some(CmdResult::NeedPoint);
|
||||
}
|
||||
}
|
||||
match &mut self.step {
|
||||
Step::Distance => {
|
||||
if t.eq_ignore_ascii_case("t") || t.eq_ignore_ascii_case("through") {
|
||||
return Some(self.advance_from_distance(None));
|
||||
}
|
||||
if upper == "E" || upper == "ERASE" {
|
||||
self.awaiting = Some(Await::Erase);
|
||||
return Some(CmdResult::NeedPoint);
|
||||
}
|
||||
if upper == "L" || upper == "LAYER" {
|
||||
self.awaiting = Some(Await::Layer);
|
||||
return Some(CmdResult::NeedPoint);
|
||||
}
|
||||
if let Some(d) = crate::entities::common::parse_typed_length(&t) {
|
||||
return Some(self.accept_distance(d));
|
||||
}
|
||||
@@ -804,15 +930,20 @@ impl CadCommand for OffsetCommand {
|
||||
*multiple = true;
|
||||
return Some(CmdResult::NeedPoint);
|
||||
}
|
||||
if !t.is_empty() {
|
||||
if let Some(d) = crate::entities::common::parse_typed_length(&t) {
|
||||
let d = d.abs().max(1e-9);
|
||||
defaults::set_offset_dist(d);
|
||||
*locked = Some(d);
|
||||
}
|
||||
let Some(d) = crate::entities::common::parse_typed_length(&t) else {
|
||||
return Some(CmdResult::NeedPoint);
|
||||
};
|
||||
let mag = d.abs().max(1e-9);
|
||||
defaults::set_offset_dist(mag);
|
||||
*locked = Some(mag);
|
||||
// The cursor has already chosen the side, so the value places
|
||||
// the offset now: on the cursor's side, or the other side when
|
||||
// negative. Before the cursor has been seen, the click still
|
||||
// chooses.
|
||||
match self.cursor {
|
||||
Some(cursor) => Some(self.place(cursor, d < 0.0)),
|
||||
None => Some(CmdResult::NeedPoint),
|
||||
}
|
||||
// Stay on the side step — the click chooses which side.
|
||||
Some(CmdResult::NeedPoint)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
@@ -863,66 +994,11 @@ impl CadCommand for OffsetCommand {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let (locked, targets, multiple) = match &self.step {
|
||||
Step::PickSide {
|
||||
locked,
|
||||
targets,
|
||||
multiple,
|
||||
} => (*locked, targets.clone(), *multiple),
|
||||
|
||||
_ => return CmdResult::NeedPoint,
|
||||
};
|
||||
// Each target offsets by its own through-distance (or the locked
|
||||
// magnitude), toward the clicked side.
|
||||
let per_target: Vec<(Handle, Vec<EntityType>)> = targets
|
||||
.iter()
|
||||
.filter_map(|entity| {
|
||||
let mag = locked.unwrap_or_else(|| perp_distance(entity, pt.as_vec3()));
|
||||
if mag < 1e-9 {
|
||||
return None;
|
||||
}
|
||||
let offsets = compute_offsets(entity, mag, pt.as_vec3());
|
||||
(!offsets.is_empty()).then(|| (entity.common().handle, offsets))
|
||||
})
|
||||
.collect();
|
||||
let mut news: Vec<EntityType> = per_target
|
||||
.iter()
|
||||
.flat_map(|(_, offsets)| offsets.iter().cloned())
|
||||
.collect();
|
||||
if news.is_empty() {
|
||||
return CmdResult::NeedPoint;
|
||||
}
|
||||
self.made += 1;
|
||||
// Erase option: the offset replaces its source object. Only the
|
||||
// objects picked from the drawing qualify — a Multiple chain's
|
||||
// targets are the offsets just made, which are not replaced again.
|
||||
let erase = self.erase_source && self.targets_are_sources;
|
||||
self.targets_are_sources = false;
|
||||
if multiple {
|
||||
// Multiple mode chains from the result just created, matching
|
||||
// OFFSET's repeated fixed-distance behavior (parallel/concentric
|
||||
// series instead of duplicate copies at the first offset).
|
||||
self.step = Step::PickSide {
|
||||
targets: news.clone(),
|
||||
locked,
|
||||
multiple: true,
|
||||
};
|
||||
} else {
|
||||
// Classic loop (#418): commit this offset and go back to the object
|
||||
// pick at the same distance, until Enter / Esc finishes.
|
||||
self.step = Step::SelectObject { locked };
|
||||
}
|
||||
if erase {
|
||||
return CmdResult::ReplaceManyContinue(per_target);
|
||||
}
|
||||
if news.len() == 1 {
|
||||
CmdResult::CommitEntity(news.pop().unwrap())
|
||||
} else {
|
||||
CmdResult::CommitEntities(news)
|
||||
}
|
||||
self.place(pt, false)
|
||||
}
|
||||
|
||||
fn on_preview_wires(&mut self, pt: DVec3) -> Vec<WireModel> {
|
||||
self.cursor = Some(pt);
|
||||
if let Step::ReferenceSecond { first } = &self.step {
|
||||
return vec![WireModel::solid(
|
||||
"offset_reference_distance".into(),
|
||||
@@ -979,6 +1055,17 @@ impl CadCommand for OffsetCommand {
|
||||
let d = defaults::get_offset_dist();
|
||||
self.accept_distance(d)
|
||||
}
|
||||
// Opened on a pre-selection, the same Enter offsets by the last
|
||||
// distance on the cursor's side.
|
||||
Step::PickSide { .. } if self.drag_start => {
|
||||
let Some(cursor) = self.cursor else {
|
||||
return CmdResult::NeedPoint;
|
||||
};
|
||||
if let Step::PickSide { locked, .. } = &mut self.step {
|
||||
*locked = Some(defaults::get_offset_dist());
|
||||
}
|
||||
self.place(cursor, false)
|
||||
}
|
||||
_ => CmdResult::Cancel,
|
||||
}
|
||||
}
|
||||
@@ -1100,3 +1187,127 @@ mod option_tests {
|
||||
assert!(matches!(cmd.on_point(DVec3::new(5.0, 5.0, 0.0)), CmdResult::CommitEntity(_)));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod drag_tests {
|
||||
use super::*;
|
||||
use codec::types::Vector2;
|
||||
|
||||
fn line_y0() -> EntityType {
|
||||
let mut line = codec::entities::Line::from_coords(0.0, 0.0, 0.0, 10.0, 0.0, 0.0);
|
||||
line.common.handle = Handle::new(1);
|
||||
EntityType::Line(line)
|
||||
}
|
||||
|
||||
/// OFFSET opened on `target`, with the cursor hovering at `cursor`.
|
||||
fn dragging(target: EntityType, cursor: [f64; 2]) -> OffsetCommand {
|
||||
let mut cmd = OffsetCommand::with_selection(vec![target.clone()], vec![target]);
|
||||
cmd.on_preview_wires(DVec3::new(cursor[0], cursor[1], 0.0));
|
||||
cmd
|
||||
}
|
||||
|
||||
fn committed_line_y(result: Option<CmdResult>) -> f64 {
|
||||
match result {
|
||||
Some(CmdResult::CommitEntity(EntityType::Line(l))) => l.start.y,
|
||||
_ => panic!("expected one committed line"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preselection_opens_on_the_live_preview() {
|
||||
let mut cmd = OffsetCommand::with_selection(vec![line_y0()], vec![line_y0()]);
|
||||
assert!(matches!(cmd.step, Step::PickSide { locked: None, .. }));
|
||||
assert_eq!(cmd.options().into_iter().map(|o| o.keyword).collect::<Vec<_>>(), ["M", "E", "L", ""]);
|
||||
// The preview passes through the cursor before anything is typed.
|
||||
assert_eq!(cmd.on_preview_wires(DVec3::new(5.0, 3.0, 0.0)).len(), 1);
|
||||
assert_eq!(cmd.dyn_live_value(DVec3::new(5.0, 3.0, 0.0)), Some(3.0));
|
||||
// A click still offsets through the cursor.
|
||||
assert!(matches!(cmd.on_point(DVec3::new(5.0, 3.0, 0.0)), CmdResult::CommitEntity(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typed_distance_places_on_the_cursor_side() {
|
||||
let mut cmd = dragging(line_y0(), [5.0, 3.0]);
|
||||
assert!((committed_line_y(cmd.on_text_input("2")) - 2.0).abs() < 1e-9);
|
||||
// The loop continues at the typed distance.
|
||||
assert!(matches!(cmd.step, Step::SelectObject { locked: Some(d) } if d == 2.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negative_distance_places_on_the_other_side() {
|
||||
let mut cmd = dragging(line_y0(), [5.0, 3.0]);
|
||||
assert!((committed_line_y(cmd.on_text_input("-2")) + 2.0).abs() < 1e-9);
|
||||
assert_eq!(defaults::get_offset_dist(), 2.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negative_distance_inside_a_circle_grows_it() {
|
||||
let circle = EntityType::Circle(CircleEnt {
|
||||
center: codec::types::Vector3::new(0.0, 0.0, 0.0),
|
||||
radius: 10.0,
|
||||
..Default::default()
|
||||
});
|
||||
let mut cmd = dragging(circle.clone(), [3.0, 0.0]);
|
||||
match cmd.on_text_input("-1") {
|
||||
Some(CmdResult::CommitEntity(EntityType::Circle(c))) => assert!((c.radius - 11.0).abs() < 1e-9),
|
||||
_ => panic!("expected one committed circle"),
|
||||
}
|
||||
let mut cmd = dragging(circle, [3.0, 0.0]);
|
||||
match cmd.on_text_input("1") {
|
||||
Some(CmdResult::CommitEntity(EntityType::Circle(c))) => assert!((c.radius - 9.0).abs() < 1e-9),
|
||||
_ => panic!("expected one committed circle"),
|
||||
}
|
||||
}
|
||||
|
||||
// A U whose arms sit 1 apart. From inside the left arm the nearest wall
|
||||
// faces the gap; mirroring the cursor its full 1.9 across would land in
|
||||
// the right arm, back inside the loop. -0.25 must still go outward.
|
||||
#[test]
|
||||
fn negative_distance_does_not_mirror_across_a_gap() {
|
||||
let u = EntityType::LwPolyline(LwPolyline {
|
||||
vertices: [
|
||||
[0.0, 0.0], [9.0, 0.0], [9.0, 10.0], [5.0, 10.0],
|
||||
[5.0, 2.0], [4.0, 2.0], [4.0, 10.0], [0.0, 10.0],
|
||||
]
|
||||
.iter()
|
||||
.map(|&[x, y]| LwVertex::new(Vector2::new(x, y)))
|
||||
.collect(),
|
||||
is_closed: true,
|
||||
..Default::default()
|
||||
});
|
||||
let mut cmd = dragging(u, [2.1, 6.0]);
|
||||
match cmd.on_text_input("-0.25") {
|
||||
Some(CmdResult::CommitEntity(EntityType::LwPolyline(p))) => {
|
||||
let min_x = p.vertices.iter().map(|v| v.location.x).fold(f64::INFINITY, f64::min);
|
||||
assert!((min_x + 0.25).abs() < 1e-6, "offset went inward: min x {min_x}");
|
||||
}
|
||||
_ => panic!("expected one committed polyline"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enter_repeats_the_last_distance_on_the_cursor_side() {
|
||||
defaults::set_offset_dist(4.0);
|
||||
let mut cmd = dragging(line_y0(), [5.0, -1.0]);
|
||||
match cmd.on_enter() {
|
||||
CmdResult::CommitEntity(EntityType::Line(l)) => assert!((l.start.y + 4.0).abs() < 1e-9),
|
||||
_ => panic!("expected one committed line"),
|
||||
}
|
||||
// After that, Enter finishes as before.
|
||||
assert!(matches!(cmd.on_enter(), CmdResult::Cancel));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn erase_is_offered_without_the_distance_step() {
|
||||
let mut cmd = dragging(line_y0(), [5.0, 3.0]);
|
||||
cmd.on_text_input("E");
|
||||
cmd.on_text_input("Y");
|
||||
assert!(matches!(cmd.on_text_input("2"), Some(CmdResult::ReplaceManyContinue(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn without_preselection_the_distance_comes_first() {
|
||||
let cmd = OffsetCommand::new(vec![line_y0()]);
|
||||
assert!(matches!(cmd.step, Step::Distance));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user