@@ -13,7 +13,7 @@ class TranslateOperation extends BaseOperation {
1313 } ,
1414 } ;
1515
16- constructor ( { weas, vector = new THREE . Vector3 ( ) } ) {
16+ constructor ( { weas, vector = new THREE . Vector3 ( ) , constraintType = null , axis = null , planeNormal = null } ) {
1717 super ( weas ) ;
1818 // currentFrame
1919 this . currentFrame = weas . avr . currentFrame ;
@@ -25,6 +25,26 @@ class TranslateOperation extends BaseOperation {
2525 vector = new THREE . Vector3 ( vector [ 0 ] , vector [ 1 ] , vector [ 2 ] ) ;
2626 }
2727 this . vector = vector . clone ( ) ;
28+ this . constraintType = constraintType || null ; // null | "axis" | "plane" | "normal"
29+ this . axis = axis ? axis . clone ( ) : new THREE . Vector3 ( ) ;
30+ this . normal = new THREE . Vector3 ( ) ;
31+ this . planeNormal = new THREE . Vector3 ( ) ;
32+ this . planeU = new THREE . Vector3 ( ) ;
33+ this . planeV = new THREE . Vector3 ( ) ;
34+ this . distance = 0 ;
35+ this . uDistance = 0 ;
36+ this . vDistance = 0 ;
37+ if ( this . constraintType === "axis" && this . axis . lengthSq ( ) === 0 ) {
38+ this . axis . set ( 1 , 0 , 0 ) ;
39+ }
40+ if ( this . constraintType === "normal" && planeNormal ) {
41+ this . normal . copy ( planeNormal ) ;
42+ }
43+ if ( this . constraintType === "plane" && planeNormal ) {
44+ this . planeNormal . copy ( planeNormal ) ;
45+ }
46+ this . refreshConstraintStateFromVector ( ) ;
47+ this . uiFields = this . buildUISchema ( ) ;
2848 }
2949
3050 execute ( ) {
@@ -50,6 +70,109 @@ class TranslateOperation extends BaseOperation {
5070 this . undo ( ) ;
5171 } ) ;
5272 }
73+
74+ buildUISchema ( ) {
75+ if ( this . constraintType === "axis" ) {
76+ return {
77+ title : "Translate (Axis)" ,
78+ fields : {
79+ distance : { type : "number" , min : - 10 , max : 10 , step : 0.1 } ,
80+ axisX : { type : "number" , min : - 1 , max : 1 , step : 0.01 , path : "axis.x" } ,
81+ axisY : { type : "number" , min : - 1 , max : 1 , step : 0.01 , path : "axis.y" } ,
82+ axisZ : { type : "number" , min : - 1 , max : 1 , step : 0.01 , path : "axis.z" } ,
83+ } ,
84+ } ;
85+ }
86+ if ( this . constraintType === "normal" ) {
87+ return {
88+ title : "Translate (Normal)" ,
89+ fields : {
90+ distance : { type : "number" , min : - 10 , max : 10 , step : 0.1 } ,
91+ normalX : { type : "number" , min : - 1 , max : 1 , step : 0.01 , path : "normal.x" } ,
92+ normalY : { type : "number" , min : - 1 , max : 1 , step : 0.01 , path : "normal.y" } ,
93+ normalZ : { type : "number" , min : - 1 , max : 1 , step : 0.01 , path : "normal.z" } ,
94+ } ,
95+ } ;
96+ }
97+ if ( this . constraintType === "plane" ) {
98+ return {
99+ title : "Translate (Plane)" ,
100+ fields : {
101+ u : { type : "number" , min : - 10 , max : 10 , step : 0.1 , path : "uDistance" } ,
102+ v : { type : "number" , min : - 10 , max : 10 , step : 0.1 , path : "vDistance" } ,
103+ normalX : { type : "number" , min : - 1 , max : 1 , step : 0.01 , path : "planeNormal.x" } ,
104+ normalY : { type : "number" , min : - 1 , max : 1 , step : 0.01 , path : "planeNormal.y" } ,
105+ normalZ : { type : "number" , min : - 1 , max : 1 , step : 0.01 , path : "planeNormal.z" } ,
106+ } ,
107+ } ;
108+ }
109+ return this . constructor . ui || null ;
110+ }
111+
112+ applyParams ( params ) {
113+ super . applyParams ( params ) ;
114+ this . refreshVectorFromConstraint ( ) ;
115+ }
116+
117+ refreshConstraintStateFromVector ( ) {
118+ if ( this . constraintType === "axis" ) {
119+ this . normalizeAxis ( this . axis ) ;
120+ this . distance = this . vector . dot ( this . axis ) ;
121+ return ;
122+ }
123+ if ( this . constraintType === "normal" ) {
124+ this . normalizeAxis ( this . normal , new THREE . Vector3 ( 0 , 0 , 1 ) ) ;
125+ this . distance = this . vector . dot ( this . normal ) ;
126+ return ;
127+ }
128+ if ( this . constraintType === "plane" ) {
129+ this . ensurePlaneBasis ( ) ;
130+ this . uDistance = this . vector . dot ( this . planeU ) ;
131+ this . vDistance = this . vector . dot ( this . planeV ) ;
132+ return ;
133+ }
134+ }
135+
136+ refreshVectorFromConstraint ( ) {
137+ if ( this . constraintType === "axis" ) {
138+ this . normalizeAxis ( this . axis ) ;
139+ this . vector . copy ( this . axis ) . multiplyScalar ( this . distance || 0 ) ;
140+ return ;
141+ }
142+ if ( this . constraintType === "normal" ) {
143+ this . normalizeAxis ( this . normal , new THREE . Vector3 ( 0 , 0 , 1 ) ) ;
144+ this . vector . copy ( this . normal ) . multiplyScalar ( this . distance || 0 ) ;
145+ return ;
146+ }
147+ if ( this . constraintType === "plane" ) {
148+ this . ensurePlaneBasis ( ) ;
149+ this . vector
150+ . copy ( this . planeU )
151+ . multiplyScalar ( this . uDistance || 0 )
152+ . add ( this . planeV . clone ( ) . multiplyScalar ( this . vDistance || 0 ) ) ;
153+ }
154+ }
155+
156+ normalizeAxis ( axis , fallback = new THREE . Vector3 ( 1 , 0 , 0 ) ) {
157+ if ( ! axis || axis . lengthSq ( ) === 0 ) {
158+ axis . copy ( fallback ) ;
159+ }
160+ axis . normalize ( ) ;
161+ }
162+
163+ ensurePlaneBasis ( ) {
164+ this . normalizeAxis ( this . planeNormal , new THREE . Vector3 ( 0 , 0 , 1 ) ) ;
165+ const n = this . planeNormal ;
166+ const ref = Math . abs ( n . x ) < 0.9 ? new THREE . Vector3 ( 1 , 0 , 0 ) : new THREE . Vector3 ( 0 , 1 , 0 ) ;
167+ const u = new THREE . Vector3 ( ) . crossVectors ( n , ref ) ;
168+ if ( u . lengthSq ( ) === 0 ) {
169+ u . crossVectors ( n , new THREE . Vector3 ( 0 , 0 , 1 ) ) ;
170+ }
171+ u . normalize ( ) ;
172+ const v = new THREE . Vector3 ( ) . crossVectors ( n , u ) . normalize ( ) ;
173+ this . planeU . copy ( u ) ;
174+ this . planeV . copy ( v ) ;
175+ }
53176}
54177
55178class RotateOperation extends BaseOperation {
0 commit comments