PolygonEditor.hx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  1. package hide.prefab;
  2. import hxd.Key as K;
  3. import hrt.prefab.Context;
  4. enum ColorState{
  5. None;
  6. Overlapped;
  7. OverlappedForDelete;
  8. Selected;
  9. }
  10. class Edge{
  11. public var p1 : h2d.col.Point;
  12. public var p2 : h2d.col.Point;
  13. public function new(p1, p2){
  14. this.p1 = p1;
  15. this.p2 = p2;
  16. }
  17. }
  18. class SphereHandle extends h3d.scene.Mesh {
  19. public function new(prim, mat, parent) {
  20. super(prim, mat, parent);
  21. }
  22. var tmp = new h3d.Vector();
  23. override function sync(ctx:h3d.scene.RenderContext) {
  24. var cam = ctx.camera;
  25. var gpos = getAbsPos().getPosition();
  26. var distToCam = cam.pos.sub(gpos).length();
  27. var engine = h3d.Engine.getCurrent();
  28. var ratio = 18 / engine.height;
  29. // Ignore parent scale
  30. parent.getAbsPos().getScale(tmp);
  31. var scale = ratio * distToCam * Math.tan(cam.fovY * 0.5 * Math.PI / 180.0);
  32. scaleX = scale / tmp.x;
  33. scaleY = scale / tmp.y;
  34. scaleZ = scale / tmp.z;
  35. calcAbsPos();
  36. super.sync(ctx);
  37. }
  38. }
  39. class MovablePoint {
  40. public var showDebug : Bool;
  41. public var point : h2d.col.Point;
  42. var mesh: h3d.scene.Mesh;
  43. public var colorState = None;
  44. var localPosText : h2d.ObjectFollower;
  45. var worldPosText : h2d.ObjectFollower;
  46. public function new(point : h2d.col.Point, ctx : Context){
  47. this.point = point;
  48. mesh = new SphereHandle(h3d.prim.Cube.defaultUnitCube(), null, ctx.local3d);
  49. mesh.name = "_movablePoint";
  50. mesh.material.setDefaultProps("ui");
  51. mesh.material.mainPass.depthTest = Always;
  52. mesh.scale(0.1);
  53. mesh.setPosition(point.x, point.y, 0);
  54. localPosText = createText(ctx);
  55. worldPosText = createText(ctx);
  56. worldPosText.offsetZ = (0.3);
  57. localPosText.offsetZ = (0.6);
  58. updateText(ctx);
  59. }
  60. function createText(ctx : Context){
  61. var o = new h2d.ObjectFollower(mesh, @:privateAccess ctx.local2d.getScene());
  62. var t = new h2d.Text(hxd.res.DefaultFont.get(), o);
  63. t.textColor = 0xFFFFFF;
  64. t.textAlign = Center;
  65. t.dropShadow = { dx : 1.5, dy : 1.5, color : 0x202020, alpha : 1.0 };
  66. return o;
  67. }
  68. public function dispose(){
  69. mesh.remove();
  70. worldPosText.remove();
  71. localPosText.remove();
  72. }
  73. function worldToScreen(wx: Float, wy: Float, wz: Float, ctx : Context) {
  74. var s2d = @:privateAccess ctx.local2d.getScene();
  75. var camera = @:privateAccess ctx.local3d.getScene().camera;
  76. camera.update();
  77. var pt = camera.project(wx, wy, wz, s2d.width, s2d.height);
  78. return new h2d.col.Point( pt.x, pt.y);
  79. }
  80. public function updateText(ctx : Context){
  81. inline function getText(o) : h2d.Text{
  82. return Std.downcast(o.getChildAt(0), h2d.Text);
  83. }
  84. getText(localPosText).visible = showDebug;
  85. getText(worldPosText).visible = showDebug;
  86. var pointWorldPos = new h3d.col.Point(point.x, point.y, 0.);
  87. ctx.local3d.localToGlobal(pointWorldPos);
  88. getText(localPosText).text = "Local : " + untyped point.x.toFixed(3) + " / " + untyped point.y.toFixed(3);
  89. getText(worldPosText).text = "World : " + untyped pointWorldPos.x.toFixed(3) + " / " + untyped pointWorldPos.y.toFixed(3) + " / " + untyped pointWorldPos.z.toFixed(3);
  90. }
  91. public function updateColor(){
  92. switch(colorState){
  93. case None : mesh.material.color.set(0,0,0);
  94. case Overlapped : mesh.material.color.set(1,1,0);
  95. case OverlappedForDelete : mesh.material.color.set(1,0,0);
  96. case Selected : mesh.material.color.set(0,0,1);
  97. }
  98. }
  99. public function interset(ray : h3d.col.Ray) : Bool{
  100. return mesh.getCollider().rayIntersection(ray, false) != -1;
  101. }
  102. public function setColorState(s : ColorState){
  103. colorState = s;
  104. }
  105. }
  106. class PolygonEditor {
  107. public var editContext : EditContext;
  108. public var showDebug : Bool;
  109. public var gridSize = 1;
  110. public var showTriangles : Bool = false;
  111. public var worldSnap = false;
  112. var polygonPrefab : hrt.prefab.l3d.Polygon;
  113. var undo : hide.ui.UndoHistory;
  114. var interactive : h2d.Interactive;
  115. var lineGraphics : h3d.scene.Graphics;
  116. var triangleGraphics : h3d.scene.Graphics;
  117. var movablePoints : Array<MovablePoint> = [];
  118. var selectedPoints : Array<h2d.col.Point> = [];
  119. var lastPointSelected : h2d.col.Point;
  120. var lastPos : h3d.col.Point;
  121. var selectedEdge : Edge;
  122. var selectedEdgeGraphic : h3d.scene.Graphics;
  123. //var lastClickStamp = 0.0;
  124. var editMode = false;
  125. // Temp container for Undo
  126. var beforeMoveList : Array<h2d.col.Point> = [];
  127. var afterMoveList : Array<h2d.col.Point> = [];
  128. public function new( polygonPrefab , undo : hide.ui.UndoHistory ){
  129. this.polygonPrefab = polygonPrefab;
  130. this.undo = undo;
  131. }
  132. public function dispose(){
  133. reset();
  134. }
  135. function removeGraphics(g : h3d.scene.Graphics){
  136. if(g != null){
  137. g.clear();
  138. g.remove();
  139. }
  140. }
  141. public function reset(){
  142. clearMovablePoints();
  143. clearSelectedPoint();
  144. if(interactive != null) interactive.remove();
  145. removeGraphics(lineGraphics);
  146. removeGraphics(selectedEdgeGraphic);
  147. removeGraphics(triangleGraphics);
  148. }
  149. inline function getContext(){
  150. return editContext.getContext(polygonPrefab);
  151. }
  152. inline function refreshInteractive() {
  153. editContext.scene.editor.refreshInteractive(polygonPrefab);
  154. }
  155. public function update( ?propName : String) {
  156. if(propName == "showDebug"){
  157. for(mp in movablePoints){
  158. mp.showDebug = showDebug;
  159. mp.updateText(getContext());
  160. }
  161. }
  162. else if(propName == "showTriangles") {
  163. drawTriangles(showTriangles);
  164. }
  165. else if(propName == "editMode") {
  166. setSelected(getContext(), true);
  167. } else {
  168. refreshInteractive();
  169. }
  170. }
  171. function copyArray(array : Array<h2d.col.Point>){
  172. var copy : Array<h2d.col.Point> = [];
  173. for(p in array)
  174. copy.push(p.clone());
  175. return copy;
  176. }
  177. function addUndo( prev : Array<h2d.col.Point>, next : Array<h2d.col.Point>){
  178. undo.change(Custom(function(undo) {
  179. var prevList = prev;
  180. var newList = next;
  181. if(undo)
  182. polygonPrefab.points = prevList;
  183. else
  184. polygonPrefab.points = newList;
  185. refreshPolygon();
  186. }));
  187. }
  188. function refreshPolygon(withProps=false) {
  189. if(!polygonPrefab.points.isClockwise())
  190. polygonPrefab.points.reverse(); // Ensure poly is always clockwise
  191. var polyPrim = polygonPrefab.generateCustomPolygon();
  192. var mesh : h3d.scene.Mesh = cast getContext().local3d;
  193. mesh.primitive = polyPrim;
  194. refreshEditorDisplay(withProps);
  195. }
  196. function refreshDebugDisplay(){
  197. for(mp in movablePoints)
  198. mp.updateText(getContext());
  199. }
  200. function clearSelectedPoint(){
  201. selectedPoints.splice(0, selectedPoints.length);
  202. lastPointSelected = null;
  203. }
  204. function isAlreadySelected( p : h2d.col.Point ) : Bool {
  205. if( p == null) return false;
  206. for( point in selectedPoints )
  207. if( point == p ) return true;
  208. return false;
  209. }
  210. function addSelectedPoint( p : h2d.col.Point ){
  211. if( p == null) return;
  212. for( point in selectedPoints )
  213. if( point == p ) return;
  214. selectedPoints.push(p);
  215. }
  216. function removePoint( p : h2d.col.Point) {
  217. polygonPrefab.points.remove(p);
  218. refreshPolygon();
  219. }
  220. function addPointOnEdge( pos: h2d.col.Point, e : Edge) {
  221. if(e == null){
  222. polygonPrefab.points.points.push(pos);
  223. return;
  224. }
  225. function findIndex(p) : Int {
  226. for(i in 0 ... polygonPrefab.points.length)
  227. if( p == polygonPrefab.points[i])
  228. return i;
  229. return -1;
  230. }
  231. var i1 = findIndex(e.p1);
  232. var i2 = findIndex(e.p2);
  233. if( hxd.Math.abs(i1 - i2) > 1 )
  234. polygonPrefab.points.points.push(pos);
  235. else
  236. polygonPrefab.points.points.insert(Std.int(hxd.Math.max(i1,i2)), pos);
  237. refreshPolygon();
  238. }
  239. function projectToGround( ray: h3d.col.Ray) {
  240. var minDist = -1.;
  241. var normal = getContext().local3d.getAbsPos().up();
  242. var plane = h3d.col.Plane.fromNormalPoint(normal.toPoint(), new h3d.col.Point(getContext().local3d.getAbsPos().tx, getContext().local3d.getAbsPos().ty, getContext().local3d.getAbsPos().tz));
  243. var pt = ray.intersect(plane);
  244. if(pt != null) { minDist = pt.sub(ray.getPos()).length();}
  245. return minDist;
  246. }
  247. function screenToWorld( u : Float, v : Float ) {
  248. var camera = @:privateAccess getContext().local3d.getScene().camera;
  249. var ray = camera.rayFromScreen(u, v);
  250. var dist = projectToGround(ray);
  251. return dist >= 0 ? ray.getPoint(dist) : null;
  252. }
  253. function trySelectPoint( ray: h3d.col.Ray ) : MovablePoint {
  254. for(mp in movablePoints)
  255. if(mp.interset(ray))
  256. return mp;
  257. return null;
  258. }
  259. function trySelectEdge( pos : h2d.col.Point ) : Edge {
  260. inline function crossProduct( a : h2d.col.Point, b : h2d.col.Point ){
  261. return a.x * b.y - a.y * b.x;
  262. }
  263. inline function dist(s1 : h2d.col.Point, s2 : h2d.col.Point, p : h2d.col.Point){
  264. var l = s2.distance(s1);
  265. l = l * l;
  266. if(l == 0) return p.distance(s1);
  267. var t = hxd.Math.max(0, hxd.Math.min(1, p.sub(s1).dot(s2.sub(s1)) / l));
  268. var proj = s1.add((s2.sub(s1).multiply(t)));
  269. return p.distance(proj);
  270. }
  271. if(polygonPrefab.points.length < 2) return null;
  272. var minDist = dist(polygonPrefab.points[0], polygonPrefab.points[polygonPrefab.points.length - 1], pos);
  273. var edge : Edge = new Edge(polygonPrefab.points[0],polygonPrefab.points[polygonPrefab.points.length - 1]);
  274. for(i in 1 ... polygonPrefab.points.length){
  275. var p1 = polygonPrefab.points[i-1];
  276. var p2 = polygonPrefab.points[i];
  277. var dist = dist(p1, p2, pos);
  278. if(dist < minDist){
  279. edge.p1 = p1;
  280. edge.p2 = p2;
  281. minDist = dist;
  282. }
  283. }
  284. return edge;
  285. }
  286. function getFinalPos( mouseX, mouseY ){
  287. var worldPos = screenToWorld(mouseX, mouseY);
  288. var localPos = getContext().local3d.globalToLocal(worldPos);
  289. if( K.isDown( K.CTRL ) ){ // Snap To Grid with Ctrl
  290. var gridPos = new h3d.col.Point();
  291. if( worldSnap ){
  292. var absPos = getContext().local3d.getAbsPos();
  293. worldPos = getContext().local3d.localToGlobal(worldPos);
  294. gridPos.x = hxd.Math.round(localPos.x / gridSize) * gridSize;
  295. gridPos.y = hxd.Math.round(localPos.y / gridSize) * gridSize;
  296. gridPos.z = hxd.Math.round(localPos.z / gridSize) * gridSize;
  297. gridPos = getContext().local3d.globalToLocal(gridPos);
  298. }
  299. else{
  300. gridPos.x = hxd.Math.round(worldPos.x / gridSize) * gridSize;
  301. gridPos.y = hxd.Math.round(worldPos.y / gridSize) * gridSize;
  302. gridPos.z = hxd.Math.round(worldPos.z / gridSize) * gridSize;
  303. }
  304. localPos = gridPos;
  305. }
  306. return localPos;
  307. }
  308. public function setSelected( ctx : Context, b : Bool ) {
  309. reset();
  310. if(!editMode) return;
  311. if(b){
  312. var s2d = @:privateAccess ctx.local2d.getScene();
  313. interactive = new h2d.Interactive(10000, 10000, s2d);
  314. interactive.propagateEvents = true;
  315. interactive.cancelEvents = false;
  316. lineGraphics = new h3d.scene.Graphics(ctx.local3d);
  317. lineGraphics.lineStyle(2, 0xFFFFFF);
  318. lineGraphics.material.mainPass.setPassName("overlay");
  319. lineGraphics.material.mainPass.depth(false, LessEqual);
  320. selectedEdgeGraphic = new h3d.scene.Graphics(ctx.local3d);
  321. selectedEdgeGraphic.lineStyle(3, 0xFFFF00, 0.5);
  322. selectedEdgeGraphic.material.mainPass.setPassName("overlay");
  323. selectedEdgeGraphic.material.mainPass.depth(false, LessEqual);
  324. triangleGraphics = new h3d.scene.Graphics(ctx.local3d);
  325. triangleGraphics.lineStyle(2, 0xFF0000);
  326. triangleGraphics.material.mainPass.setPassName("overlay");
  327. triangleGraphics.material.mainPass.depth(false, LessEqual);
  328. refreshEditorDisplay();
  329. drawTriangles(showTriangles);
  330. interactive.onWheel = function(e) {
  331. refreshDebugDisplay();
  332. };
  333. interactive.onKeyDown =
  334. function(e) {
  335. e.propagate = false;
  336. if( K.isDown( K.SHIFT ) ){
  337. clearSelectedPoint();
  338. var ray = @:privateAccess ctx.local3d.getScene().camera.rayFromScreen(s2d.mouseX, s2d.mouseY);
  339. refreshMovablePoints(ray);
  340. if(lastPos == null) lastPos = getFinalPos(s2d.mouseX, s2d.mouseY);
  341. refreshSelectedEdge(new h2d.col.Point(lastPos.x, lastPos.y));
  342. }
  343. }
  344. interactive.onKeyUp =
  345. function(e) {
  346. e.propagate = false;
  347. var ray = @:privateAccess ctx.local3d.getScene().camera.rayFromScreen(s2d.mouseX, s2d.mouseY);
  348. refreshMovablePoints(ray);
  349. if(lastPos == null) lastPos = getFinalPos(s2d.mouseX, s2d.mouseY);
  350. refreshSelectedEdge(new h2d.col.Point(lastPos.x, lastPos.y));
  351. }
  352. interactive.onPush =
  353. function(e) {
  354. var finalPos = getFinalPos(s2d.mouseX, s2d.mouseY);
  355. var ray = @:privateAccess ctx.local3d.getScene().camera.rayFromScreen(s2d.mouseX, s2d.mouseY);
  356. if( K.isDown( K.MOUSE_LEFT ) ){
  357. e.propagate = false;
  358. // Shift + Left Click : Remove Point
  359. if( K.isDown( K.SHIFT ) ){
  360. var mp = trySelectPoint(ray);
  361. if(mp != null){
  362. var prevList = copyArray(polygonPrefab.points.points);
  363. removePoint(mp.point);
  364. var newList = copyArray(polygonPrefab.points.points);
  365. addUndo(prevList, newList);
  366. }
  367. }
  368. else {
  369. // Left Click : Add/Set selected point / Clear selection
  370. lastPos = finalPos.clone();
  371. var mp = trySelectPoint(ray);
  372. if(mp != null){
  373. if( K.isDown(K.ALT) && !isAlreadySelected(mp.point))
  374. addSelectedPoint(mp.point);
  375. lastPointSelected = mp.point;
  376. beforeMoveList = copyArray(polygonPrefab.points.points);
  377. }
  378. // Double Left Click : Create point
  379. else{
  380. clearSelectedPoint();
  381. if(K.isDown(K.CTRL)) {
  382. // var curStamp = haxe.Timer.stamp();
  383. // var diff = curStamp - lastClickStamp;
  384. // if(diff < 0.2){
  385. var prevList = copyArray(polygonPrefab.points.points);
  386. var pt = new h2d.col.Point(finalPos.x, finalPos.y);
  387. addPointOnEdge(pt, selectedEdge);
  388. var newList = copyArray(polygonPrefab.points.points);
  389. addUndo(prevList, newList);
  390. refreshSelectedEdge(new h2d.col.Point(finalPos.x, finalPos.y));
  391. // Select new point
  392. lastPointSelected = pt;
  393. }
  394. //lastClickStamp = curStamp;
  395. }
  396. refreshMovablePoints();
  397. }
  398. }
  399. };
  400. interactive.onRelease =
  401. function(e) {
  402. //lastPos = null;
  403. lastPointSelected = null;
  404. if( beforeMoveList != null ){
  405. afterMoveList = copyArray(polygonPrefab.points.points);
  406. addUndo(beforeMoveList, afterMoveList);
  407. beforeMoveList = null;
  408. afterMoveList = null;
  409. }
  410. };
  411. interactive.onMove =
  412. function(e) {
  413. var ray = @:privateAccess ctx.local3d.getScene().camera.rayFromScreen(s2d.mouseX, s2d.mouseY);
  414. var finalPos = getFinalPos(s2d.mouseX, s2d.mouseY);
  415. refreshMovablePoints(ray);
  416. refreshSelectedEdge(new h2d.col.Point(finalPos.x, finalPos.y));
  417. if( K.isDown( K.MOUSE_LEFT )){
  418. var move : h2d.col.Point = null;
  419. var pos = new h2d.col.Point(finalPos.x, finalPos.y);
  420. if(lastPointSelected != null){
  421. move = pos.sub(lastPointSelected);
  422. lastPointSelected.load(pos);
  423. for(p in selectedPoints){
  424. if(lastPointSelected == p) continue;
  425. p.x += move.x; p.y += move.y;
  426. }
  427. }
  428. refreshMovablePoints();
  429. refreshSelectedEdge(new h2d.col.Point(finalPos.x, finalPos.y));
  430. refreshPolygon(false);
  431. lastPos = finalPos.clone();
  432. }
  433. else
  434. refreshDebugDisplay();
  435. };
  436. }
  437. else
  438. editMode = false;
  439. }
  440. function refreshSelectedEdge( pos : h2d.col.Point ){
  441. selectedEdge = trySelectEdge(pos);
  442. selectedEdgeGraphic.clear();
  443. if(K.isDown( K.SHIFT ) )
  444. return;
  445. if(selectedEdge != null){
  446. selectedEdgeGraphic.moveTo(selectedEdge.p1.x, selectedEdge.p1.y, 0);
  447. selectedEdgeGraphic.lineTo(selectedEdge.p2.x, selectedEdge.p2.y, 0);
  448. }
  449. }
  450. function drawTriangles( b : Bool ){
  451. triangleGraphics.clear();
  452. if(b && polygonPrefab.getPrimitive(getContext()) != null){
  453. var i = 0;
  454. var prim = polygonPrefab.getPrimitive(getContext());
  455. while(i < prim.idx.length){
  456. triangleGraphics.moveTo(prim.points[prim.idx[i]].x, prim.points[prim.idx[i]].y, 0);
  457. triangleGraphics.lineTo(prim.points[prim.idx[i + 1]].x, prim.points[prim.idx[i + 1]].y, 0);
  458. triangleGraphics.lineTo(prim.points[prim.idx[i + 2]].x, prim.points[prim.idx[i + 2]].y, 0);
  459. triangleGraphics.lineTo(prim.points[prim.idx[i]].x, prim.points[prim.idx[i]].y, 0);
  460. i += 3;
  461. }
  462. }
  463. }
  464. function clearMovablePoints(){
  465. for(mp in movablePoints)
  466. mp.dispose();
  467. movablePoints.splice(0, movablePoints.length);
  468. }
  469. function createMovablePoints(){
  470. for(p in polygonPrefab.points){
  471. var mp = new MovablePoint(p, getContext());
  472. movablePoints.push(mp);
  473. }
  474. }
  475. function refreshMovablePoints( ?ray ){
  476. for(mp in movablePoints)
  477. mp.setColorState(None);
  478. if(ray != null){
  479. var mp = trySelectPoint(ray);
  480. if( mp != null && mp.colorState != Selected)
  481. K.isDown( K.SHIFT ) ? mp.setColorState(OverlappedForDelete) : mp.setColorState(Overlapped);
  482. }
  483. for(p in selectedPoints)
  484. for(mp in movablePoints)
  485. if(mp.point == p){
  486. mp.setColorState(Selected);
  487. break;
  488. }
  489. for(mp in movablePoints){
  490. if( mp.point == lastPointSelected) mp.setColorState(Selected);
  491. mp.updateColor();
  492. mp.showDebug = showDebug;
  493. mp.updateText(getContext());
  494. }
  495. }
  496. function refreshEditorDisplay(withProps=true) {
  497. lineGraphics.clear();
  498. clearMovablePoints();
  499. if(polygonPrefab.points == null || polygonPrefab.points.length == 0) return;
  500. lineGraphics.moveTo(polygonPrefab.points[polygonPrefab.points.length - 1].x, polygonPrefab.points[polygonPrefab.points.length - 1].y, 0);
  501. for(p in polygonPrefab.points)
  502. lineGraphics.lineTo(p.x, p.y, 0);
  503. createMovablePoints();
  504. refreshMovablePoints();
  505. if(withProps)
  506. refreshPointList(editContext.getCurrentProps(polygonPrefab));
  507. }
  508. public function addProps( ctx : EditContext ){
  509. var props = new hide.Element('
  510. <div class="poly-editor">
  511. <div class="group" name="Tool">
  512. <div align="center">
  513. <input type="button" value="Edit Mode : Disabled" class="editModeButton" />
  514. </div>
  515. <div class="description">
  516. <i>Ctrl + Left Click</i> : Add point on edge <br>
  517. <i>Shift + Left Click</i> : Delete selected point <br>
  518. Drag with <i>Left Click</i> : Move selected points <br>
  519. Drag with <i>Left Click + Ctrl</i> : Move selected points on grid <br>
  520. <i>Alt + Left Click</i> : Add point to selection
  521. </div>
  522. <dt>Show Debug</dt><dd><input type="checkbox" field="showDebug"/></dd>
  523. <dt>Show Triangles</dt><dd><input type="checkbox" field="showTriangles"/></dd>
  524. <dt>Grid Size</dt><dd><input type="range" min="0" max="10" field="gridSize"/></dd>
  525. <dt>World Snap</dt><dd><input type="checkbox" field="worldSnap"/></dd>
  526. </div>
  527. <div align="center">
  528. <div class="group" name="Points">
  529. <div class="point-list"> </div>
  530. <input type="button" value="Reset" class="reset" />
  531. </div>
  532. </div>
  533. </div>');
  534. var editModeButton = props.find(".editModeButton");
  535. editModeButton.click(function(_) {
  536. editMode = !editMode;
  537. editModeButton.val(editMode ? "Edit Mode : Enabled" : "Edit Mode : Disabled");
  538. editModeButton.toggleClass("editModeEnabled", editMode);
  539. setSelected(getContext(), true);
  540. if(!editMode)
  541. refreshInteractive();
  542. });
  543. props.find(".reset").click(function(_) {
  544. var prevList = copyArray(polygonPrefab.points.points);
  545. polygonPrefab.points.points.splice(0, polygonPrefab.points.points.length);
  546. var nextList = copyArray(polygonPrefab.points.points);
  547. addUndo(prevList, nextList);
  548. refreshPolygon();
  549. });
  550. refreshPointList(props);
  551. ctx.properties.add(props, this, function(pname) {ctx.onChange(polygonPrefab, pname); });
  552. return props;
  553. }
  554. function refreshPointList( props : hide.Element){
  555. var container = props.find(".point-list");
  556. container.empty();
  557. function createVector(p : h2d.col.Point){
  558. var v = new Element('<div class="poly-vector2" >');
  559. var deleteButton = new Element('<input type="button" value="-" class="deletePoint" />');
  560. var fieldX = new Element('<input class="inputX" type="text" name="xfield" minlength="1" maxlength="4">');
  561. var fieldY = new Element('<input type="text" name="yfield" minlength="1" maxlength="4">');
  562. fieldX.val(p.x);
  563. fieldY.val(p.y);
  564. fieldX.on("input", function(_) {
  565. var prevValue = p.x;
  566. p.x = Std.parseFloat(fieldX.val());
  567. var nextValue = p.x;
  568. undo.change(Custom(function(undo) {
  569. p.x = undo ? prevValue : nextValue;
  570. refreshPolygon();
  571. }));
  572. refreshPolygon();
  573. });
  574. fieldY.on("input", function(_) {
  575. var prevValue = p.y;
  576. p.y = Std.parseFloat(fieldY.val());
  577. var nextValue = p.y;
  578. undo.change(Custom(function(undo) {
  579. p.y = undo ? prevValue : nextValue;
  580. refreshPolygon();
  581. }));
  582. refreshPolygon();
  583. });
  584. deleteButton.on("click", function(_) {
  585. var prevList = copyArray(polygonPrefab.points.points);
  586. polygonPrefab.points.points.remove(p);
  587. var nextList = copyArray(polygonPrefab.points.points);
  588. addUndo(prevList, nextList);
  589. refreshPolygon();
  590. refreshPointList(props);
  591. });
  592. v.append('<label>X </label>');
  593. v.append(fieldX);
  594. v.append('<label> Y </label>');
  595. v.append(fieldY);
  596. v.append(deleteButton);
  597. v.append('</div>');
  598. container.append(v);
  599. }
  600. if(polygonPrefab.points != null) {
  601. for(p in polygonPrefab.points){
  602. createVector(p);
  603. }
  604. }
  605. }
  606. }