diff --git a/editor/extensions/src/apply-wheelsprung-fixes.ts b/editor/extensions/src/apply-wheelsprung-fixes.ts index 8e9c6c7c..4706c432 100644 --- a/editor/extensions/src/apply-wheelsprung-fixes.ts +++ b/editor/extensions/src/apply-wheelsprung-fixes.ts @@ -1,6 +1,9 @@ /// import { ensureWindingOrder } from "./ensure-winding-order"; +import { polylineToPolygon } from "./outline-polyline"; +import { newPolygon } from "./new-polygon"; + export function applyWheelsprungFixes(mapOrLayer) { tiled.log("Running applyWheelsprungFixes:" + mapOrLayer); @@ -23,6 +26,16 @@ export function applyWheelsprungFixes(mapOrLayer) { ); object.polygon = ensureWindingOrder(object.polygon); } + + if (object.shape == MapObject.Polyline && object.selected) { + var polygon = polylineToPolygon(object.polygon, 4.0); + var newObject = newPolygon(object); + tiled.log("New object:" + newObject); + tiled.log("new polygon:" + polygon); + newObject.polygon = polygon; + mapOrLayer.addObject(newObject); + }; + if(object.shape == MapObject.Ellipse && object.className == "DynamicObject") { if(object.width != object.height) { tiled.log("Ellipses not supported for Dynamic Objects. Making circular:" + object); diff --git a/editor/extensions/src/new-polygon.ts b/editor/extensions/src/new-polygon.ts new file mode 100644 index 00000000..f0b496f6 --- /dev/null +++ b/editor/extensions/src/new-polygon.ts @@ -0,0 +1,9 @@ +export function newPolygon(oldObj) { + var object = new MapObject(); + object.x = oldObj.x; + object.y = oldObj.y; + object.width = oldObj.width; + object.height = oldObj.height; + object.shape = MapObject.Polygon; + return object; +} diff --git a/editor/extensions/src/outline-polyline.ts b/editor/extensions/src/outline-polyline.ts new file mode 100644 index 00000000..8acc47e1 --- /dev/null +++ b/editor/extensions/src/outline-polyline.ts @@ -0,0 +1,138 @@ +function getOffsets(a, b, thickness) { + var + dx = b.x - a.x, + dy = b.y - a.y, + len = Math.sqrt(dx * dx + dy * dy), + scale = thickness / (2 * len) + ; + return { + x: -scale * dy, + y: scale * dx + }; +} + +function getIntersection(a1, b1, a2, b2) { + + // directional constants + var + k1 = (b1.y - a1.y) / (b1.x - a1.x), + k2 = (b2.y - a2.y) / (b2.x - a2.x); + + + + // if the directional constants are equal, the lines are parallel + if (Math.abs(k1 - k2)<0.00001) { + return; + } + + // y offset constants for both lines + var m1 = a1.y - k1 * a1.x; + var m2 = a2.y - k2 * a2.x; + + // compute x + var x = (m1 - m2) / (k2 - k1); + + // use y = k * x + m to get y coordinate + var y = k1 * x + m1; + + return { x:x, y:y }; +} + +function isArray(obj){ + return (Object.prototype.toString.call(obj)==='[object Array]'); +} + +function me(points, thickness) { + + + //Convert points into json notation + var arr = []; + for (var i=0; i [pt.x, pt.y]); + tiled.log("Running me:" + points); + var vertices = me(points, thickness); + return vertices.map((pt) => Qt.point(pt[0], pt[1])); +} \ No newline at end of file diff --git a/editor/extensions/wheelsprung/index.js b/editor/extensions/wheelsprung/index.js index 429e740d..74bdc1cf 100644 --- a/editor/extensions/wheelsprung/index.js +++ b/editor/extensions/wheelsprung/index.js @@ -19,6 +19,103 @@ return vertices; } + // src/outline-polyline.ts + function getOffsets(a, b, thickness) { + var dx = b.x - a.x, dy = b.y - a.y, len = Math.sqrt(dx * dx + dy * dy), scale = thickness / (2 * len); + return { + x: -scale * dy, + y: scale * dx + }; + } + function getIntersection(a1, b1, a2, b2) { + var k1 = (b1.y - a1.y) / (b1.x - a1.x), k2 = (b2.y - a2.y) / (b2.x - a2.x); + if (Math.abs(k1 - k2) < 1e-5) { + return; + } + var m1 = a1.y - k1 * a1.x; + var m2 = a2.y - k2 * a2.x; + var x = (m1 - m2) / (k2 - k1); + var y = k1 * x + m1; + return { x, y }; + } + function isArray(obj) { + return Object.prototype.toString.call(obj) === "[object Array]"; + } + function me(points, thickness) { + var arr = []; + for (var i2 = 0; i2 < points.length; i2++) { + var pt = points[i2]; + arr.push({ + x: pt[0], + y: pt[1] + }); + } + points = arr; + if (!isArray(thickness)) { + var t2 = thickness; + thickness = []; + for (var i2 = 0; i2 < points.length; i2++) { + thickness.push(t2); + } + } + var off, off2, poly = [], isFirst, isLast, prevA, prevB, interA, interB, p0a, p1a, p0b, p1b; + for (var i2 = 0, il = points.length - 1; i2 < il; i2++) { + isFirst = !i2; + isLast = i2 === points.length - 2; + off = getOffsets(points[i2], points[i2 + 1], thickness[i2]); + off2 = getOffsets(points[i2], points[i2 + 1], thickness[i2 + 1]); + p0a = { x: points[i2].x + off.x, y: points[i2].y + off.y }; + p1a = { x: points[i2 + 1].x + off2.x, y: points[i2 + 1].y + off2.y }; + p0b = { x: points[i2].x - off.x, y: points[i2].y - off.y }; + p1b = { x: points[i2 + 1].x - off2.x, y: points[i2 + 1].y - off2.y }; + if (!isFirst) { + interA = getIntersection(prevA[0], prevA[1], p0a, p1a); + if (interA) { + poly.unshift(interA); + } + interB = getIntersection(prevB[0], prevB[1], p0b, p1b); + if (interB) { + poly.push(interB); + } + } + if (isFirst) { + poly.unshift(p0a); + poly.push(p0b); + } + if (isLast) { + poly.unshift(p1a); + poly.push(p1b); + } + if (!isLast) { + prevA = [p0a, p1a]; + prevB = [p0b, p1b]; + } + } + for (var i2 = 0; i2 < poly.length; i2++) { + var pt = poly[i2]; + poly[i2] = [pt.x, pt.y]; + } + poly.push(poly[0]); + return poly; + } + function polylineToPolygon(polygon, thickness) { + var points = polygon.map((pt) => [pt.x, pt.y]); + tiled.log("Running me:" + points); + var vertices = me(points, thickness); + return vertices.map((pt) => Qt.point(pt[0], pt[1])); + } + + // src/new-polygon.ts + function newPolygon(oldObj) { + var object = new MapObject(); + object.x = oldObj.x; + object.y = oldObj.y; + object.width = oldObj.width; + object.height = oldObj.height; + object.shape = MapObject.Polygon; + return object; + } + // src/apply-wheelsprung-fixes.ts function applyWheelsprungFixes(mapOrLayer) { tiled.log("Running applyWheelsprungFixes:" + mapOrLayer); @@ -42,6 +139,15 @@ ); object.polygon = ensureWindingOrder(object.polygon); } + if (object.shape == MapObject.Polyline && object.selected) { + var polygon = polylineToPolygon(object.polygon, 4); + var newObject = newPolygon(object); + tiled.log("New object:" + newObject); + tiled.log("new polygon:" + polygon); + newObject.polygon = polygon; + mapOrLayer.addObject(newObject); + } + ; if (object.shape == MapObject.Ellipse && object.className == "DynamicObject") { if (object.width != object.height) { tiled.log("Ellipses not supported for Dynamic Objects. Making circular:" + object); diff --git a/src/level_meta/level_data.nim b/src/level_meta/level_data.nim index a3f38786..fec219f3 100644 --- a/src/level_meta/level_data.nim +++ b/src/level_meta/level_data.nim @@ -48,7 +48,7 @@ let officialLevels*: OrderedTable[Path, LevelMeta] = @[ newLevelMeta(name= "Novel Navigations", path="levels/novel_navigations.flatty", theme= Bookshelf, hash="83D025E12A1140BC3B86831F20FF4622"), newLevelMeta(name= "Shelf Swinger", path="levels/shelf_swinger.flatty", theme= Bookshelf, hash="E91EE82424E715F5C6F9A439F1096F1B"), newLevelMeta(name= "Under Covers", path="levels/under_covers.flatty", theme= Bookshelf, hash="07C4F9B28FA27A1EDB706A9432544979"), - newLevelMeta(name= "Rollercoaster", path="levels/rollercoaster.flatty", theme= Bookshelf, hash="92330B17118385D29D0549DB1A823312"), + newLevelMeta(name= "Rollercoaster", path="levels/rollercoaster.flatty", theme= Bookshelf, hash="EB073CA2B150DF7EF20C31E8ECB01F86"), newLevelMeta(name= "Hooked", path="levels/hooked.flatty", theme= Bookshelf, hash="C83A58916A6F97F1AA0A2952E12BFABA"), newLevelMeta(name= "Dominoes", path="levels/dominoes.flatty", theme= Bookshelf, hash="19A67112C03DA92C9D51FB268956F93C"), newLevelMeta(name= "Backflip", path="levels/backflip.flatty", theme= Bookshelf, hash="513A5202C29C322690EE9B33068D6789"), @@ -64,11 +64,11 @@ let officialLevels*: OrderedTable[Path, LevelMeta] = @[ newLevelMeta(name= "Treasure Tunnel", path="levels/treasure_tunnel.flatty", theme= Plants, hash="6E5F2C44C7ED48F25ED86E834C68149B"), newLevelMeta(name= "Half-Pipe Jungle", path="levels/halfpipe.flatty", theme= Plants, hash="31609EFBA4AB9DEFD10A4BF90CB5300D"), newLevelMeta(name= "Rickety Bridge", path="levels/rickety_bridge.flatty", theme= Plants, hash="0CF980DB09C2EE873C16836319B51CBD"), - newLevelMeta(name = "Rolling Raiders", path="levels/boulder.flatty", theme= Plants, hash="0541EF41C104AE11FFCE43B3B3077A85"), + newLevelMeta(name = "Rolling Raiders", path="levels/boulder.flatty", theme= Plants, hash="7A93A4DD90103EF4363EB503BFAF0642"), newLevelMeta(name= "Zig Zag Down", path="levels/zig_zag_down.flatty", theme= Desk, hash="633154C6A64C4C0EB4FCC1FE1F0490B0"), newLevelMeta(name = "Time Traveler", path="levels/time_traveler.flatty", theme= Desk, hash="18193FB4744661DA83906690CE211469"), - newLevelMeta(name = "Tight Squeeze", path="levels/tight_squeeze.flatty", theme= Desk, hash="0541EF41C104AE11FFCE43B3B3077A85"), + newLevelMeta(name = "Tight Squeeze", path="levels/tight_squeeze.flatty", theme= Desk, hash="7A93A4DD90103EF4363EB503BFAF0642"), newLevelMeta(name= "Ballistic Bowler", path="levels/ballistic_bowler.flatty", theme= Desk, hash="E7ED841B154ADD2B5694AD6613CCE5D1"), newLevelMeta(name= "Ramp of Pages", path="levels/leg_up.flatty", theme= Desk, hash="6F48907EB90D6985CB66486C4A11FFEC"), newLevelMeta(name= "Ballancing Act", path="levels/ballancing_act.flatty", theme= Desk, hash="15B0EC58F7BB8A9912DB470DE0CCA81F"), diff --git a/support/levels b/support/levels index 79b320f2..2d0238a4 160000 --- a/support/levels +++ b/support/levels @@ -1 +1 @@ -Subproject commit 79b320f2083502ea35b8e91d0ce0cf915f6648d2 +Subproject commit 2d0238a400060d3994de3daa9cca7dfd0aecac62