def recursive_intersect(self, p, tmin=0): """ binary recursion stack starts unwinding when reach bileaf node p * p.l.is_leaf and p.r.is_leaf recursive argument changes: * p <- p.l tmin <- tminL * p <- p.r tmin <- tminR * http://stackoverflow.com/questions/12468251/convert-recursion-to-iteration * http://stackoverflow.com/questions/7548026/convert-recursive-binary-tree-traversal-to-iterative """ assert p.is_operation miss = intersect_miss(p, self.ray, tmin) tminL = tmin tminR = tmin loopcount = 0 ctrl = (CtrlLoopLeft | CtrlLoopRight) while ctrl & (CtrlLoopLeft | CtrlLoopRight ): if ctrl & CtrlLoopLeft: if p.l.is_leaf: left = intersect_primitive(p.l, self.ray, tminL) else: left = self.recursive_intersect(p.l, tmin=tminL) pass pass if ctrl & CtrlLoopRight: if p.r.is_leaf: right = intersect_primitive(p.r, self.ray, tminR) else: right = self.recursive_intersect(p.r, tmin=tminR) pass pass ctrl = self.binary_ctrl(p.operation, left, right, tminL, tminR) if ctrl == CtrlLoopLeft: tminL = left.t + self.epsilon elif ctrl == CtrlLoopRight: tminR = right.t + self.epsilon else: pass # will fall out the ctrl loop pass loopcount += 1 assert loopcount < 10 pass # end while ctrl loop assert ctrl in [CtrlReturnMiss, CtrlReturnLeft, CtrlReturnRight, CtrlReturnFlipRight ] result = self.binary_result(ctrl, miss, left, right, tmin) if self.debug: p.recursive = result return result
def recursive_intersect_2(self, p, tmin=0): if p.is_leaf: return intersect_primitive(p, self.ray, tmin) pass miss = intersect_miss(p, self.ray, tmin) tminL = tmin tminR = tmin loopcount = 0 ctrl = (CtrlLoopLeft | CtrlLoopRight) while ctrl & (CtrlLoopLeft | CtrlLoopRight ): if ctrl & CtrlLoopLeft: left = self.recursive_intersect_2(p.l, tmin=tminL) pass if ctrl & CtrlLoopRight: right = self.recursive_intersect_2(p.r, tmin=tminR) pass ctrl = self.binary_ctrl(p.operation, left, right, tminL, tminR) if ctrl == CtrlLoopLeft: tminL = left.t + self.epsilon elif ctrl == CtrlLoopRight: tminR = right.t + self.epsilon else: pass # will fall out the ctrl loop pass loopcount += 1 assert loopcount < 10 pass # end while ctrl loop assert ctrl in [CtrlReturnMiss, CtrlReturnLeft, CtrlReturnRight, CtrlReturnFlipRight ] result = self.binary_result(ctrl, miss, left, right, tmin) if self.debug: p.recursive = result return result
def Intersect(self): """ # the below handling of a operation holding primitives # initially seems a special case cop out, subverting the iterative approach, # that is liable to to work for simple trees, but not for complex ones # # BUT on deeper refeclection that isnt the case, need to allow to keep going left until # find primitives one level below in order to get the ball rolling # and start filling the primStack, as go back upwards """ action = self.action assert action in [GotoLft, GotoRgh] if self.node.is_primitive: tt, nn, name, act = intersect_primitive(self.node, self.ray, self.tmin) if action == GotoLft: self.tl = tt self.nl = nn self.lname = name self.lact = act if self.debug > 3: log.info("pr.Intersect.GotoLft %s tl %5.2f lname %s " % (self.node.name, self.tl if self.tl else -1, self.lname)) pass elif action == GotoRgh: self.tr = tt self.nr = nn self.rname = name self.ract = act if self.debug > 3: log.info("pr.Intersect.GotoRgh %s tr %5.2f rname %s " % (self.node.name, self.tr if self.tr else -1, self.rname)) pass pass self.action = Compute self.node = self.node.parent elif self.node.is_operation: gotoL = intersectBox(self.node.left) gotoR = intersectBox(self.node.right) if gotoL and self.node.left.is_primitive: self.record_traversal(self.node.left) tt, nn, name, act = intersect_primitive( self.node.left, self.ray, self.tmin) self.tl = tt self.nl = nn self.lname = name self.lact = act gotoL = False if self.debug > 3: log.info("op.Intersect.gotoL %s tl %5.2f lname %s " % (self.node.left.name, self.tl, self.lname)) if gotoR and self.node.right.is_primitive: self.record_traversal(self.node.right) tt, nn, name, act = intersect_primitive( self.node.right, self.ray, self.tmin) self.tr = tt self.nr = nn self.rname = name self.ract = act gotoR = False if self.debug > 3: log.info("op.Intersect.gotoR %s tr %5.2f rname %s " % (self.node.right.name, self.tr, self.rname)) # immediate right/left primitives are not stacked, as are ready for compute if gotoL or gotoR: if gotoL: # non-primitive subtree intersect self.primStack.push( (self.tl, self.nl, self.lname, self.lact), debug=self.debug > 1) self.actionStack.push(LoadLft, debug=self.debug > 1) elif gotoR: self.primStack.push( (self.tr, self.nr, self.rname, self.ract), debug=self.debug > 1) self.actionStack.push(LoadRgh, debug=self.debug > 1) pass else: # both gotoL and gotoR False means miss OR both prim intersects done, so are ready for compute self.tminStack.push(self.tmin, debug=self.debug > 1) self.actionStack.push(LoadLft, debug=self.debug > 1) self.actionStack.push(SaveLft, debug=self.debug > 1) pass # NB not the same as doing this interleaved within the above, as this places # no demands on (gotoL or gotoR) if gotoL: self.action = GotoLft elif gotoR: self.action = GotoRgh else: self.action = Compute pass if self.debug > 3: log.info( "Intersect -> %s tr/tl %5.2f/%5.2f rname/lname %s/%s " % (desc_action(self.action), self.tr if self.tr else -1, self.tl if self.tl else -1, self.rname, self.lname)) else: assert 0
def iterative_intersect(self, root): """ Iterative CSG boolean intersection * https://www.hackerearth.com/practice/notes/iterative-tree-traversals/ """ assert self.typ == "ITERATIVE" self.top = root self.node = root if self.node.is_primitive: return intersect_primitive(self.node, self.ray, self.tmin) self.count = 0 limit = 10 self.actionStack.push(Compute) #self.actionStack.push(GotoLft) self.action = GotoLft do_goto = False # ignoring the first GotoLft avoids the virtual root and associated termination complications while self.actionStack.count() > 0: self.count += 1 #self.action = self.actionStack.pop(debug=self.debug > 2) if self.action == SaveLft: self.SaveLft_() self.action = GotoRgh if self.action == GotoLft or self.action == GotoRgh: if do_goto: self.GoTo() pass do_goto = True if self.action == GotoLft or self.action == GotoRgh: self.Intersect() if self.action == LoadLft or self.action == LoadRgh: self.Load_() self.action = Compute if self.action == Compute: self.Compute_() pass if self.action == Return: break if self.count == limit: log.fatal("iray %d ray %s count %d reaches limit %d " % (self.iray, self.ray, self.count, limit)) assert 0 pass return self.Return_()
def recursive_intersect(self, root, depth=0, tmin=0): """ * minimizing use of member vars makes recursive algorithm easier to understand * instead use local vars, which will have different existance at the different levels of the recursion * can think of member vars as effective globals wrt the recursion * loopers result in recursive call repeating the same node, with tmin advanced * recursively never traverse the root again ? are always going down with root.left root.right never going up ? * although the traverse never goes up, completion of the recursive instance calls does go back up once hitting primitives in the leaves """ assert root assert self.typ == "RECURSIVE" if depth == 0: self.top = root pass self.node = root # above are just for debug comparison against iterative algo, not used below if root.is_primitive: return intersect_primitive(root, self.ray, tmin) elif root.is_operation: tl, nl, lname, lact = self.recursive_intersect(root.left, depth=depth + 1, tmin=tmin) tr, nr, rname, ract = self.recursive_intersect(root.right, depth=depth + 1, tmin=tmin) root.rstack = (tl, nl, lname, lact, tr, nr, rname, ract ) # for debug comparison with iterative loopcount = 0 looplimit = 10 while loopcount < looplimit: loopcount += 1 stateL = self.classify(tl, nl, tmin) # tmin_l tmin_r ? stateR = self.classify(tr, nr, tmin) acts = boolean_table(root.operation, stateL, stateR) opr = "%s(%s:%s,%s:%s)" % (desc[root.operation], lname, desc_state[stateL], rname, desc_state[stateR]) act_RetMiss = (RetMiss & acts) act_RetL = (RetL & acts) act_RetR = (RetR & acts) act_LoopL = (LoopL & acts) act_LoopR = (LoopR & acts) act_RetLIfCloser = ((RetLIfCloser & acts) and tl <= tr) act_LoopLIfCloser = ((LoopLIfCloser & acts) and tl <= tr) act_RetRIfCloser = ((RetRIfCloser & acts) and tr < tl) act_LoopRIfCloser = ((LoopRIfCloser & acts) and tr < tl) trep = self.trep_fmt(tmin, tl, tr) ret = () if act_RetMiss: act = RetMiss ret = None, None, None, act elif act_RetL or act_RetLIfCloser: act = RetLIfCloser if act_RetLIfCloser else RetL ret = tl, nl, lname, act elif act_RetR or act_RetRIfCloser: act = RetRIfCloser if act_RetRIfCloser else RetR if (FlipR & acts): nr = -nr ret = tr, nr, rname, act elif act_LoopL or act_LoopLIfCloser: act = LoopLIfCloser if act_LoopLIfCloser else LoopL tl, nl, lname, _ = self.recursive_intersect(root.left, depth=depth + 1, tmin=tl) elif act_LoopR or act_LoopRIfCloser: act = LoopRIfCloser if act_LoopRIfCloser else LoopR tr, nr, rname, _ = self.recursive_intersect(root.right, depth=depth + 1, tmin=tr) else: log.fatal("[%d] RECURSIVE UNHANDLED acts " % (loopcount)) assert 0 self.act = act if self.debug > 1: log.info("(%d)[%d] RECURSIVE %s : %s -> %s : %s " % (self.iray, loopcount, root.name, opr, desc_acts(self.act), trep)) if len(ret) > 0: return ret else: # only Loop-ers hang aroud here to intersect again with advanced tmin, the rest return up to caller assert act in [LoopLIfCloser, LoopL, LoopRIfCloser, LoopR] pass else: log.fatal( " depth %d root %s root.is_operation %d root.is_primitive %d " % (depth, root, root.is_operation, root.is_primitive)) assert 0 pass log.fatal("[%d] RECURSIVE count EXCEEDS LIMIT %d " % (loopcount, looplimit)) assert 0 return None, None, None, 0
def iterative_intersect(partBuffer, ray, tst): """ For following code paths its simpler to instrument rays, not intersects as isects keep getting created, pushed, popped, etc.. """ debug = tst.debug postorder_sequence = [0x1, 0x132, 0x1376254, 0x137fe6dc25ba498] ierr = 0 abort_ = False instrument = True partOffset = 0 numParts = len(partBuffer) primIdx = -1 fullHeight = ffs_(numParts + 1) - 2 height = fullHeight - 1 postorder = postorder_sequence[height] numInternalNodes = (0x1 << (1 + height)) - 1 _tmin = np.zeros((TRANCHE_STACK_SIZE), dtype=np.float32) _tranche = np.zeros((TRANCHE_STACK_SIZE), dtype=np.uint32) tranche = -1 csg_ = [CSG_(), CSG_()] lhs = csg_[LHS] rhs = csg_[RHS] lhs.curr = -1 rhs.curr = -1 isect = np.zeros([4, 4, 4], dtype=np.float32) tranche = TRANCHE_PUSH0(_tranche, _tmin, tranche, POSTORDER_SLICE(0, numInternalNodes), ray.tmin) while tranche >= 0: tranche, tmp, tmin = TRANCHE_POP0(_tranche, _tmin, tranche) begin = POSTORDER_BEGIN(tmp) end = POSTORDER_END(tmp) i = begin while i < end: nodeIdx = POSTORDER_NODE(postorder, i) leftIdx = nodeIdx * 2 rightIdx = nodeIdx * 2 + 1 depth = TREE_DEPTH(nodeIdx) subNodes = (0x1 << (1 + height - depth)) - 1 halfNodes = (subNodes - 1) / 2 bileaf = leftIdx > numInternalNodes operation = partBuffer[nodeIdx - 1, Q1, W].view(np.uint32) tX_min = np.zeros(2, dtype=np.float32) tX_min[LHS] = tmin tX_min[RHS] = tmin x_state = np.zeros(2, dtype=np.uint32) if bileaf: isect[LEFT, 0, W] = 0. isect[RIGHT, 0, W] = 0. isect[LEFT] = intersect_primitive( Node.fromPart(partBuffer[partOffset + leftIdx - 1]), ray, tX_min[LHS]) isect[RIGHT] = intersect_primitive( Node.fromPart(partBuffer[partOffset + rightIdx - 1]), ray, tX_min[RHS]) else: try: isect[LEFT] = lhs.pop() except Error: log_info("%s : ERROR_LHS_POP_EMPTY : ABORT " % (pfx("I0", tst.iray, nodeIdx, bileaf))) ierr |= ERROR_LHS_POP_EMPTY abort_ = True break pass try: isect[RIGHT] = rhs.pop() except Error: log_info("%s : ERROR_RHS_POP_EMPTY : ABORT " % (pfx("I0", tst.iray, nodeIdx, bileaf))) ierr |= ERROR_RHS_POP_EMPTY abort_ = True break pass pass x_state[LHS] = CSG_CLASSIFY(isect[LEFT][0], ray.direction, tX_min[LHS]) x_state[RHS] = CSG_CLASSIFY(isect[RIGHT][0], ray.direction, tX_min[RHS]) ctrl = boolean_ctrl_packed_lookup( operation, x_state[LHS], x_state[RHS], isect[LEFT][0][W] <= isect[RIGHT][0][W]) if debug: log_info("%s : %s " % (pfx("I0", tst.iray, nodeIdx, bileaf), fmt(isect[LEFT], isect[RIGHT], x_state[LHS], x_state[RHS], ctrl))) if instrument: ray.addseq(ctrl) pass reiterate = False if ctrl < CTRL_.LOOP_A: ## recursive return after first classify, needs an avenue to push pass else: side = ctrl - CTRL_.LOOP_A loop = -1 while side > -1 and loop < 10: if debug: log_info( "%s : side %s loop %d " % (pfx("I", tst.iray, nodeIdx, bileaf), side, loop)) loop += 1 THIS = side + LEFT tX_min[side] = isect[THIS][0][W] + propagate_epsilon if bileaf: isect[THIS] = intersect_primitive( Node.fromPart(partBuffer[partOffset + leftIdx + side - 1]), ray, tX_min[side]) else: try: isect[THIS] = csg_[side].pop() except Error: log_info("%s : ERROR_POP_EMPTY : ABORT : %s " % (pfx("I0", tst.iray, nodeIdx, bileaf), stk(csg_[LHS], csg_[RHS]))) ierr |= ERROR_POP_EMPTY abort_ = True break pass pass x_state[side] = CSG_CLASSIFY(isect[THIS][0], ray.direction, tX_min[side]) ctrl = boolean_ctrl_packed_lookup( operation, x_state[LHS], x_state[RHS], isect[LEFT][0][W] <= isect[RIGHT][0][W]) if debug: log_info("%s : %s " % (pfx("I1", tst.iray, nodeIdx, bileaf), fmt(isect[LEFT], isect[RIGHT], x_state[LHS], x_state[RHS], ctrl))) if instrument: ray.addseq(ctrl) pass side = ctrl - CTRL_.LOOP_A ## NB possibly changed side if side > -1 and not bileaf: other = 1 - side tX_min[side] = isect[THIS][0][W] + propagate_epsilon csg_[other].push(isect[other + LEFT]) leftTree = POSTORDER_SLICE(i - 2 * halfNodes, i - halfNodes) rightTree = POSTORDER_SLICE(i - halfNodes, i) endTree = POSTORDER_SLICE( i, end) # FIX numInternalNodes -> end sideTree = leftTree if side == LHS else rightTree tranche = TRANCHE_PUSH(_tranche, _tmin, tranche, endTree, tmin) tranche = TRANCHE_PUSH(_tranche, _tmin, tranche, sideTree, tX_min[side]) reiterate = True break pass pass # side loop pass if reiterate or abort_: break pass isect[RFLIP] = isect[RIGHT] isect[RFLIP, 0, X] = -isect[RFLIP, 0, X] isect[RFLIP, 0, Y] = -isect[RFLIP, 0, Y] isect[RFLIP, 0, Z] = -isect[RFLIP, 0, Z] assert ctrl < CTRL_.LOOP_A result = isect[ctrl] nside = LHS if nodeIdx % 2 == 0 else RHS # even on left csg_[nside].push(result) i += 1 # next postorder node in the tranche pass # end traversal loop if abort_: break pass # end tranch loop LHS_curr = csg_[LHS].curr RHS_curr = csg_[RHS].curr ierr |= ERROR_LHS_END_NONEMPTY if LHS_curr != -1 else 0 ierr |= ERROR_RHS_END_EMPTY if RHS_curr != 0 else 0 assert RHS_curr == 0 and ierr == 0 assert RHS_curr == 0, RHS_curr ret = csg_[RHS].data[0] assert ret.shape == (4, 4) ret[0, W] = abs(ret[0, W]) return ret
def evaluative_intersect(partBuffer, ray, tst): debug = tst.debug partOffset = 0 numParts = len(partBuffer) primIdx = -1 fullHeight = TREE_HEIGHT(numParts) height = fullHeight - 1 numInternalNodes = TREE_NODES(height) numNodes = TREE_NODES(fullHeight) postorder_sequence = [0x1, 0x132, 0x1376254, 0x137fe6dc25ba498] try: postorder = postorder_sequence[fullHeight] except IndexError: postorder = getattr(tst.root, '_postorder_leaf', None) tr = Tranche() tr.push(POSTORDER_SLICE(0, numNodes), ray.tmin) csg = CSGD() csg.curr = -1 tloop = -1 while tr.curr > -1: tloop += 1 assert tloop < 20 # wow root3 needs lots of loops slice_, tmin = tr.pop() begin = POSTORDER_BEGIN(slice_) end = POSTORDER_END(slice_) beginIdx = POSTORDER_NODE(postorder, begin) endIdx = POSTORDER_NODE(postorder, end - 1) #if debug:log.info("%6d E : tranche begin %d end %d (nodeIdx %d:%d)tmin %5.2f tloop %d %r " % (tst.iray, begin, end, beginIdx, endIdx, tmin, tloop, csg)) i = begin while i < end: nodeIdx = POSTORDER_NODE(postorder, i) depth = TREE_DEPTH(nodeIdx) subNodes = TREE_NODES(fullHeight - depth) halfNodes = (subNodes - 1) / 2 primitive = nodeIdx > numInternalNodes operation = partBuffer[nodeIdx - 1, Q1, W].view(np.uint32) #print "(%d) depth %d subNodes %d halfNodes %d " % (nodeIdx, depth, subNodes, halfNodes ) if primitive: isect = intersect_primitive( Node.fromPart(partBuffer[partOffset + nodeIdx - 1]), ray, tmin) isect[0, W] = math.copysign(isect[0, W], -1. if nodeIdx % 2 == 0 else 1.) csg.push(isect, nodeIdx) else: if csg.curr < 1: raise Error( "ERROR_POP_EMPTY : csg.curr < 1 when need two items to combine" ) firstLeft = signbit(csg.data[csg.curr, 0, W]) secondLeft = signbit(csg.data[csg.curr - 1, 0, W]) if not firstLeft ^ secondLeft: raise Error("ERROR_XOR_SIDE") left = csg.curr if firstLeft else csg.curr - 1 right = csg.curr - 1 if firstLeft else csg.curr l_state = CSG_CLASSIFY(csg.data[left, 0], ray.direction, tmin) r_state = CSG_CLASSIFY(csg.data[right, 0], ray.direction, tmin) t_left = abs(csg.data[left, 0, W]) t_right = abs(csg.data[right, 0, W]) ctrl = boolean_ctrl_packed_lookup(operation, l_state, r_state, t_left <= t_right) ray.addseq(ctrl) if debug: log_info("%s : %s " % (pfx("E1", tst.iray, nodeIdx, None), fmt(csg.data[left], csg.data[right], l_state, r_state, ctrl))) UNDEFINED = 0 CONTINUE = 1 BREAK = 2 if ctrl < CTRL_.LOOP_A: result = np.zeros((4, 4), dtype=np.float32) if not ctrl == CTRL_.RETURN_MISS: result[:] = csg.data[left if ctrl == CTRL_.RETURN_A else right] pass if ctrl == CTRL_.RETURN_FLIP_B: result[0, X] = -result[0, X] result[0, Y] = -result[0, Y] result[0, Z] = -result[0, Z] pass result[0, W] = math.copysign(result[0, W], -1. if nodeIdx % 2 == 0 else 1.) if debug: log_info( "%s : %s " % (pfx("E2", tst.iray, nodeIdx, None), f4(result))) csg.pop() csg.pop() csg.push(result, nodeIdx) act = CONTINUE else: loopside = left if ctrl == CTRL_.LOOP_A else right otherside = right if ctrl == CTRL_.LOOP_A else left leftIdx = 2 * nodeIdx rightIdx = leftIdx + 1 otherIdx = rightIdx if ctrl == CTRL_.LOOP_A else leftIdx tminAdvanced = abs(csg.data[loopside, 0, W]) + propagate_epsilon other = np.zeros( (4, 4), dtype=np.float32 ) # need tmp as pop about to invalidate indices other[:] = csg.data[otherside] csg.pop() csg.pop() csg.push(other, otherIdx) endTree = POSTORDER_SLICE(i, end) # fix numNodes -> end leftTree = POSTORDER_SLICE(i - 2 * halfNodes, i - halfNodes) rightTree = POSTORDER_SLICE(i - halfNodes, i) loopTree = leftTree if ctrl == CTRL_.LOOP_A else rightTree tr.push(endTree, tmin) tr.push(loopTree, tminAdvanced) act = BREAK pass # return or "recursive" call if act == BREAK: break pass pass i += 1 # next postorder node in the tranche pass # end traversal loop pass # end tranch loop assert csg.curr == 0, csg.curr ret = csg.data[0] assert ret.shape == (4, 4) ret[0, W] = abs(ret[0, W]) return ret
def recursive_intersect_r(nodeIdx, tmin): """ :param nodeIdx: 1-based levelorder tree index """ leftIdx = nodeIdx * 2 rightIdx = nodeIdx * 2 + 1 bileaf = leftIdx > numInternalNodes isect = np.zeros([4, 4, 4], dtype=np.float32) tX_min = np.zeros(2, dtype=np.float32) tX_min[LHS] = tmin tX_min[RHS] = tmin x_state = np.zeros(2, dtype=np.uint32) if bileaf: isect[LEFT, 0, W] = 0. isect[RIGHT, 0, W] = 0. isect[LEFT] = intersect_primitive( Node.fromPart(partBuffer[partOffset + leftIdx - 1]), ray, tX_min[LHS]) isect[RIGHT] = intersect_primitive( Node.fromPart(partBuffer[partOffset + rightIdx - 1]), ray, tX_min[RHS]) else: isect[LEFT] = recursive_intersect_r(leftIdx, tX_min[LHS]) isect[RIGHT] = recursive_intersect_r(rightIdx, tX_min[RHS]) pass x_state[LHS] = CSG_CLASSIFY(isect[LEFT][0], ray.direction, tX_min[LHS]) x_state[RHS] = CSG_CLASSIFY(isect[RIGHT][0], ray.direction, tX_min[RHS]) operation = partBuffer[nodeIdx - 1, Q1, W].view(np.uint32) ctrl = boolean_ctrl_packed_lookup( operation, x_state[LHS], x_state[RHS], isect[LEFT][0][W] <= isect[RIGHT][0][W]) #if debug:log_info("%s : %s " % (pfx("R0",tst.iray,nodeIdx,bileaf), fmt(isect[LEFT],isect[RIGHT],x_state[LHS],x_state[RHS],ctrl))) if instrument: ray.addseq(ctrl) side = ctrl - CTRL_.LOOP_A loop = -1 while side > -1 and loop < 10: loop += 1 THIS = side + LEFT tX_min[side] = isect[THIS][0][W] + propagate_epsilon if bileaf: isect[THIS] = intersect_primitive( Node.fromPart(partBuffer[partOffset + leftIdx + side - 1]), ray, tX_min[side]) else: isect[THIS] = slavish_intersect_r(leftIdx + side, tX_min[side]) pass x_state[side] = CSG_CLASSIFY(isect[THIS][0], ray.direction, tX_min[side]) ctrl = boolean_ctrl_packed_lookup( operation, x_state[LHS], x_state[RHS], isect[LEFT][0][W] <= isect[RIGHT][0][W]) if instrument: ray.addseq(ctrl) pass side = ctrl - CTRL_.LOOP_A ## NB possibly changed side pass isect[RFLIP] = isect[RIGHT] isect[RFLIP, 0, X] = -isect[RFLIP, 0, X] isect[RFLIP, 0, Y] = -isect[RFLIP, 0, Y] isect[RFLIP, 0, Z] = -isect[RFLIP, 0, Z] assert ctrl < CTRL_.LOOP_A # recursive return is equivalent to what gets popped ? if debug: log_info("%s : %s " % (pfx("R1", tst.iray, nodeIdx, bileaf), fmt(isect[LEFT], isect[RIGHT], x_state[LHS], x_state[RHS], ctrl))) return isect[ctrl]
def iterative_intersect(self, root, depth=0, tmin=0, debug=1, icheck=True): """ :param root: :return isect: I instance 0-based postorder p-indices from p0 at leftmost to p14 at root, for height 4 tree, excluding leaf nodes to make height 3 internal nodes:: In [78]: root4.txt Out[78]: root4 14 o 6 13 o o 2 5 9 12 o o o o 0 1 3 4 7 8 10 11 o o o o o o o o o o o o o o o o o o o o o o o o Consider evaluating p13 subtree, of sub-height 2 (from full-height - depth = 3 - 1 = 2 ) * reiterate p13.left (p9) -> repeat p7, p8, p9 [with live tree Node.leftmost(p13.l)->p7 ] * reiterate p13.right (p12) -> repeat p10,p11,p12 [with live tree Node.leftmost(p13.r)->p10 ] Need to derive the p-indices that need to repeat based on the depth of p13 and the height of the tree. * sub-nodes 7, l-nodes = r-nodes = (sub-nodes - 1)/2 = (7 - 1)/2 = 3 * 13-3 +0, +1, +2 = 10, 11, 12 * 13-3*2 +0, +1, +2 = 7, 8, 9 * beneath p13 are two sub-subtrees Operation height is 3, 2^(h+1) = 2^(3+1) - 1 = 15 nodes in total Subtree starting p13 is at depth 1, 2^(h-d+1) = 2^(2+1) - 1 = 7 nodes Subtree starting p9 is at depth 2, 2^(h-d+1) = 2^(1+1) - 1 = 3 nodes """ self.check_tree(root) lhs = [] rhs = [] tranche = [] tranche.append([tmin,Node.leftmost(root),None]) if icheck: # check using 0-based postorder indices i_height = root.maxdepth - 1 # excluding leaves numInternalNodes = Node.NumNodes(i_height) itranche = [] itranche.append([tmin, 0, numInternalNodes]) i_postorder = Node.postorder_r(root, nodes=[], leaf=False) assert len(i_postorder) == numInternalNodes lmo = Node.leftmost(root) assert lmo.pidx == 0 and lmo is i_postorder[0] assert i_postorder[numInternalNodes-1].next_ == None else: itranche = None pass tcount = 0 while len(tranche) > 0: assert len(tranche) <= 4 tmin, begin, end = tranche.pop() if icheck: i_tmin, i_begin, i_end = itranche.pop() #print "icheck (%d,%d) [%s,%s] " % (i_begin, i_end, begin, end ) assert len(itranche) == len(tranche) assert i_tmin == tmin assert i_postorder[i_begin] is begin assert i_postorder[i_end-1].next_ == end pass #log.info("%s : start tranche %d begin %s end %s " % (self.pfx, tcount, begin, end)) tcount += 1 assert tcount < 10 p = begin if icheck: i_pindex = i_begin while p is not end: if icheck: assert i_postorder[i_pindex] is p i_depth = p.cdepth i_subNodes = Node.NumNodes( i_height, i_depth ) i_halfNodes = (i_subNodes - 1)/2 pass act = 0 left = None right = None miss = intersect_miss(p, self.ray, tmin) result = None tminL = tmin tminR = tmin ctrl = (CtrlLoopLeft | CtrlLoopRight) log.debug("p loop %s : %s " % (desc_ctrl(ctrl), p)) reiterate_ = False loopcount = 0 while ctrl & (CtrlLoopLeft | CtrlLoopRight ): if ctrl & CtrlLoopLeft: if p.l.is_leaf: left = intersect_primitive(p.l, self.ray, tminL) else: try: left = lhs.pop() except IndexError: log.error("%s : lhs pop from empty" % self.pfx) self.tst.ierr += 1 left = miss pass pass pass if ctrl & CtrlLoopRight: if p.r.is_leaf: right = intersect_primitive(p.r, self.ray, tminR) else: try: right = rhs.pop() except IndexError: log.error("%s : rhs pop from empty" % self.pfx) self.tst.ierr += 1 right = miss pass pass pass ctrl = self.binary_ctrl(p.operation, left, right, tminL, tminR) if ctrl in [CtrlReturnMiss, CtrlReturnLeft, CtrlReturnRight, CtrlReturnFlipRight]: pass # will fall out of the ctrl while as no longer loopers elif ctrl == CtrlLoopLeft: if self.debug: print "CtrlLoopLeft %s " % p.l pass tminL = left.t + self.epsilon if not p.l.is_leaf: rhs.append(right) tranche.append([tmin,p,None]) # should this be tminL ? its for continuation tranche.append([tminL,Node.leftmost(p.l),p.l.next_]) if icheck: itranche.append([tmin, i_index, numInternalNodes ]) itranche.append([tminL, i_index - i_halfNodes*2, i_index - i_halfNodes ]) print "icheck lhs %r " % itranche pass reiterate_ = True pass elif ctrl == CtrlLoopRight: tminR = right.t + self.epsilon if self.debug: print "CtrlLoopRight %s " % p.r pass if not p.r.is_leaf: lhs.append(left) tranche.append([tmin,p,None]) # should this be tminR ? tranche.append([tminR,Node.leftmost(p.r),p.r.next_]) if icheck: assert p.r.next_ is p # next on right is always self itranche.append([tmin, i_index, numInternalNodes ]) itranche.append([tminR, i_index - i_halfNodes, i_index ]) print "icheck rhs %r " % itranche pass reiterate_ = True pass else: assert 0, desc_ctrl(ctrl) pass if reiterate_: # non-bileaf loopers have to break to traverse subtree break pass loopcount += 1 assert loopcount < 10 pass pass # end while ctrl loop if reiterate_: #log.info("break out of postorder traversal, for re-iteration of subtree ntranche %d " % len(tranche)) break assert ctrl in [CtrlReturnMiss, CtrlReturnLeft,CtrlReturnRight,CtrlReturnFlipRight] result = self.binary_result(ctrl, miss, left, right, tmin) if p.is_left: if self.debug: log.info("%s : lhs append %d : %s " % (self.pfx, len(lhs), lhs )) lhs.append(result) else: if self.debug: log.info("%s : rhs append %d : %s " % (self.pfx, len(rhs), rhs )) rhs.append(result) pass if self.debug: p.iterative = result if icheck: i_pindex += 1 pass p = p.next_ pass # postorder tranche traversal while loop pass #assert len(lhs) == 0, lhs #assert len(rhs) == 1, rhs # end with p.idx = 1 for the root if not len(lhs) == 0: log.error("%s : lhs has %d, expect 0 " % (self.pfx, len(lhs)) ) self.tst.ierr += 1 pass if not len(rhs) == 1: log.error("%s : rhs has %d, expect 1 " % (self.pfx, len(rhs)) ) self.tst.ierr += 1 pass return rhs[0]