This commit is contained in:
Nick Winter 2014-08-28 17:08:05 -07:00
parent 0fa5d264f4
commit e67b4ae064
3 changed files with 53 additions and 53 deletions

View file

@ -92,60 +92,58 @@ module.exports.consolidateThangs = consolidateThangs = (thangs) ->
padding = 0 padding = 0
console.log 'got max width', width, 'height', height, 'left', left, 'bottom', bottom, 'of thangs', thangs.length, 'structural', structural.length if debug console.log 'got max width', width, 'height', height, 'left', left, 'bottom', bottom, 'of thangs', thangs.length, 'structural', structural.length if debug
grid = new Grid structural, width, height, padding, left, bottom grid = new Grid structural, width, height, padding, left, bottom
console.log grid.toString() if debug
# Approach: start at bottom left. Go right, then up. At each occupied grid square, find the largest rectangle we can make starting at that corner, add a corresponding Thang to the grid, and unmark all occupied grid squares.
# Since it's not like we're going to do any of these:
# http://stackoverflow.com/questions/5919298/algorithm-for-finding-the-fewest-rectangles-to-cover-a-set-of-rectangles
# http://stackoverflow.com/questions/4701887/find-the-set-of-largest-contiguous-rectangles-to-cover-multiple-areas
dissection = [] dissection = []
for y in grid.columns bottom, height addStructuralThang = (rect) ->
for x in grid.rows left, width thang = structural[dissection.length] # Grab one we already know is configured properly.
continue unless grid.grid[y][x].length
rect = largestRectangle grid, y, x, false, debug
vertices = rect.vertices()
for y2 in [vertices[0].y ... vertices[1].y] # maybe ..?
for x2 in [vertices[0].x ... vertices[2].x] # maybe ..?
grid.grid[y2][x2] = []
console.log grid.toString() if debug
thang = structural[dissection.length] # grab one we already know is configured properly
console.error 'Hmm, our dissection has more Thangs than the original structural Thangs?', dissection.length unless thang console.error 'Hmm, our dissection has more Thangs than the original structural Thangs?', dissection.length unless thang
thang.width = rect.width
thang.height = rect.height
thang.pos.x = rect.x thang.pos.x = rect.x
thang.pos.y = rect.y thang.pos.y = rect.y
thang.width = rect.width
thang.height = rect.height
thang.createBodyDef() thang.createBodyDef()
dissection.push thang dissection.push thang
dissectRectangles grid, addStructuralThang, false, debug
# Now add the new structural thangs back to thangs and return the ones not in the dissection.
console.log 'Turned', structural.length, 'structural Thangs into', dissection.length, 'dissecting Thangs.' console.log 'Turned', structural.length, 'structural Thangs into', dissection.length, 'dissecting Thangs.'
thangs.push dissection... thangs.push dissection...
structural[dissection.length ... structural.length] structural[dissection.length ... structural.length]
module.exports.largestRectangle = largestRectangle = (grid, bottomY, leftX, wantEmpty, debug) ->
# If wantEmpty, then we try to cover empty rectangles. module.exports.dissectRectangles = dissectRectangles = (grid, rectangleCallback, wantEmpty, debug) ->
# Otherwise, we try to cover occupied rectangles. # Mark Maxham's fast sweeper approach: https://github.com/codecombat/codecombat/issues/1090
coveredRows = [] console.log grid.toString() if debug
shortestCoveredRow = grid.width - leftX for x in grid.rows grid.left, grid.left + grid.width
for y in grid.columns bottomY, grid.height y = grid.clampColumn grid.bottom
coveredRow = 0 while y < grid.clampColumn grid.bottom + grid.height
for x in grid.rows leftX, leftX + shortestCoveredRow y2 = y # Note our current y.
if Boolean(grid.grid[y][x].length) isnt wantEmpty ++y2 until occ x, y2, grid, wantEmpty # Sweep through y to expand 1xN rect.
++coveredRow if y2 > y # If we get a hit, sweep X with that swath.
else x2 = x + 1
break ++x2 until occCol x2, y, y2, grid, wantEmpty
break unless coveredRow w = x2 - x
coveredRows.push coveredRow h = y2 - y
shortestCoveredRow = Math.min(shortestCoveredRow, coveredRow) rect = addRect grid, x, y, w, h, wantEmpty
console.log 'largestRectangle() for', bottomY, leftX, 'got coveredRows', coveredRows if debug rectangleCallback rect
[maxArea, maxAreaRows, maxAreaRowLength, shortestRow] = [0, 0, 0, 0] console.log grid.toString() if debug
for rowLength, rowIndex in coveredRows y = y2
shortestRow ||= rowLength ++y
area = rowLength * (rowIndex + 1)
if area > maxArea occ = (x, y, grid, wantEmpty) ->
maxAreaRows = rowIndex + 1 return true if y > grid.bottom + grid.height or x > grid.left + grid.width
maxAreaRowLength = shortestRow console.error 'trying to check invalid coordinates', x, y, 'from grid', grid.bottom, grid.left, grid.width, grid.height unless grid.grid[y]?[x]
maxArea = area Boolean(grid.grid[y][x].length) is wantEmpty
shortestRow = Math.min(rowLength, shortestRow)
console.log 'So largest rect has area', maxArea, 'with', maxAreaRows, 'rows of length', maxAreaRowLength if debug occCol = (x, y1, y2, grid, wantEmpty) ->
rect = new Rectangle leftX + maxAreaRowLength / 2, bottomY + maxAreaRows / 2, maxAreaRowLength, maxAreaRows for j in [y1 ... y2]
console.log 'That corresponds to a rectangle', rect.toString() if debug if occ(x, j, grid, wantEmpty)
rect return true
false
addRect = (grid, leftX, bottomY, width, height, wantEmpty) ->
for x in [leftX ... leftX + width]
for y in [bottomY ... bottomY + height]
grid.grid[y][x] = if wantEmpty then [true] else []
new Rectangle leftX + width / 2, bottomY + height / 2, width, height

View file

@ -77,12 +77,14 @@ module.exports = # /app/lib/surface
point: c.object {required: ['x', 'y']}, point: c.object {required: ['x', 'y']},
x: {type: 'number'} x: {type: 'number'}
y: {type: 'number'} y: {type: 'number'}
z: {type: 'number'}
'surface:choose-region': c.object {required: ['points']}, 'surface:choose-region': c.object {required: ['points']},
points: c.array {minItems: 2, maxItems: 2}, points: c.array {minItems: 2, maxItems: 2},
c.object {required: ['x', 'y']}, c.object {required: ['x', 'y']},
x: {type: 'number'} x: {type: 'number'}
y: {type: 'number'} y: {type: 'number'}
z: {type: 'number'}
'surface:new-thang-added': c.object {required: ['thang', 'sprite']}, 'surface:new-thang-added': c.object {required: ['thang', 'sprite']},
thang: {type: 'object'} thang: {type: 'object'}

View file

@ -234,7 +234,7 @@ module.exports = class LevelPlaybackView extends CocoView
button.addClass(classes[2]) if e.volume >= 1.0 button.addClass(classes[2]) if e.volume >= 1.0
onScrub: (e, options) -> onScrub: (e, options) ->
e?.preventDefault() e?.preventDefault?()
options.scrubDuration = 500 options.scrubDuration = 500
Backbone.Mediator.publish('level:set-time', options) Backbone.Mediator.publish('level:set-time', options)
@ -347,7 +347,7 @@ module.exports = class LevelPlaybackView extends CocoView
shouldIgnore: -> return @disabled or @realTime shouldIgnore: -> return @disabled or @realTime
onTogglePlay: (e) -> onTogglePlay: (e) ->
e?.preventDefault() e?.preventDefault?()
return if @shouldIgnore() return if @shouldIgnore()
button = $('#play-button') button = $('#play-button')
willPlay = button.hasClass('paused') or button.hasClass('ended') willPlay = button.hasClass('paused') or button.hasClass('ended')