Skip to content
Snippets Groups Projects
Castle.py 140 KiB
Newer Older
  • Learn to ignore specific revisions
  •             # rotate 90 horizontal, cw...
                bpy.ops.transform.rotate(value=-cPieHlf, constraint_axis=[False, False, True])
    
            # rotate 90 horizontal, ccw...
    
            else:
                bpy.ops.transform.rotate(value=cPieHlf, constraint_axis=[False, False, True])
    
            # rotate 90 forward (face plant)...
            #        bpy.ops.transform.rotate(value=cPieHlf,constraint_axis=[True,False,False])
            # rotate 90 cw along side
            #        bpy.ops.transform.rotate(value=cPieHlf,constraint_axis=[False,True,False])
    
            cSideRObj.select_set(False)  # deselect after rotate...
    
    
            return  # all done, is automatic, just a matter of detail/protocol.
    
    
    ################################################################################
    
    def makeWallObj(sRef, objScene, objLoc, objName, blockArea, openList, wallExtOpts, objMat):
    
        settings['Steps'] = wallExtOpts[0]
        settings['Shelf'] = wallExtOpts[1]
    
        meshVs, meshFs = wallBuild(sRef, wallPlan(sRef, blockArea, openList), openList)
    
        newMesh = bpy.data.meshes.new(objName)
        newMesh.from_pydata(meshVs, [], meshFs)
    
        # doesn't seem to matter...
        #    newMesh.update(calc_edges=True)
    
        newMesh.materials.append(objMat)
    
        newObj = bpy.data.objects.new(objName, newMesh)
        newObj.location.x = objLoc[0]
        newObj.location.y = objLoc[1]
        newObj.location.z = objLoc[2]
    
        objScene.objects.link(newObj)  # must do for generation/rotation
    
        return newObj
    
    
    ########################
    # Generate simple "plane" for floor objects.
    #
    # objArea[leftX,rightX,zBase,zTop,0,yDepth]
    # objDiv will subdivide plane based on sizing;
    #  - see MakeABlock(objArea,objDiv) for details.
    #
    def makePlaneObj(objName, objArea, objMat, objDiv):
        objVs = []
        objFs = []
    
        objVs, objFs = MakeABlock(objArea, objDiv)
    
        objMesh = bpy.data.meshes.new(objName)
        objMesh.from_pydata(objVs, [], objFs)
        objMesh.update()
    
        #    objMesh.update(calc_edges=True) # doesn't seem to matter...
    
        objMesh.materials.append(objMat)
    
        newObj = bpy.data.objects.new(objName, objMesh)
        newObj.location = bpy.context.scene.cursor_location
    
        return newObj
    
    
    ################################################################################
    ################
    ########################
    ##
    #
    # Blocks module/script inclusion
    # - to be reduced significantly.
    ##
    # Module notes:
    #
    # consider removing wedge crit for small "c" and "cl" values
    # wrap around for openings on radial stonework?
    # repeat for opening doesn't distribute evenly when radialized
    #  - see wrap around note above.
    # if opening width == indent*2 the edge blocks fail (row of blocks cross opening).
    # if block width variance is 0, and edging is on, right edge blocks create a "vertical seam".
    #
    ##
    #######################
    ################
    ################################################################################
    ##
    
    
    ###################################
    #
    # create lists of blocks.
    #
    # blockArea defines the "space" (vertical, horizontal, depth) to fill,
    #  and provides block height, variation, and gap/grout. May affect "door" opening.
    #
    # holeList identifies "openings" in area.
    #
    # Returns: list of rows.
    #  rows = [[center height,row height,edge offset],[...]]
    #
    def wallPlan(sRef, blockArea, holeList):
    
        rows = []
    
        blockAreaZ = blockArea[0]
        blockAreaX = blockArea[1]
        blockAreaY = blockArea[2]
        blockHMax = blockArea[3]
        blockHVar = blockArea[4]
        blockGap = blockArea[5]
    
        # this is wrong! should be...?
    
        blockHMin = BLOCK_MIN + blockGap
    
        # no variation for slopes so walls match curvature
        if sRef.properties.cXTest:
            if sRef.properties.wallSlope or sRef.properties.CTunnel:
                blockHVar = 0
    
        rowHMin = blockHMin
    
        # alternate rowHMin=blockHMax-blockHVar+blockGap
    
    
        # splits are a row division, for openings
        splits = [0]  # split bottom row
        # add a split for each critical point on each opening
        for hole in holeList:
            splits += hole.crits()
        # and, a split for the top row
        splits.append(blockAreaZ)
        splits.sort()  # needed?
    
        # divs are the normal old row divisions, add them between the top and bottom split
    
        # what's with "[1:-1]" at end????
    
        divs = fill(splits[0], splits[-1], blockHMax, blockHMin, blockHVar)[1:-1]
    
        # remove the divisions that are too close to the splits, so we don't get tiny thin rows
        for i in range(len(divs) - 1, -1, -1):
            for j in range(len(splits)):
                if abs(divs[i] - splits[j]) < rowHMin:
                    del(divs[i])
                    break
    
        # now merge the divs and splits lists
        divs += splits
        divs.sort()
    
        # trim the rows to the bottom and top of the wall
        if divs[0] < 0:
            divs[:1] = []
        if divs[-1] > blockAreaZ:
            divs[-1:] = []
    
        # process each row
        divCount = len(divs) - 1  # number of divs to check
        divCheck = 0  # current div entry
    
        while divCheck < divCount:
            RowZ = (divs[divCheck] + divs[divCheck + 1]) / 2
            RowHeight = divs[divCheck + 1] - divs[divCheck] - blockGap
            rowEdge = settings['eoff'] * (fmod(divCheck, 2) - 0.5)
    
            if RowHeight < rowHMin:  # skip row if too shallow
                del(divs[divCheck + 1])  # delete next div entry
                divCount -= 1  # Adjust count for removed div entry.
                continue
    
            rows.append(rowOb(RowZ, RowHeight, rowEdge))
    
            divCheck += 1  # on to next div entry
    
        # set up opening object to handle the edges of the wall
        WallBoundaries = OpeningInv((dims['s'] + dims['e']) / 2, blockAreaZ / 2, blockAreaX, blockAreaZ)
    
        # Go over each row in the list, set up edge blocks and block sections
        for rownum in range(len(rows)):
            rowProcessing(rows[rownum], holeList, WallBoundaries)
    
        return rows
    
    
    ################################################################################
    #
    # Build the wall, based on rows, "holeList", and parameters;
    #     geometry for the blocks, arches, steps, platforms...
    #
    # Return: verts and faces for wall object.
    #
    def wallBuild(sRef, rows, holeList):
    
        wallVs = []
        wallFs = []
    
        AllBlocks = []
    
        # create local references for anything that's used more than once...
        rowCount = len(rows)
    
        wallTop = rows[rowCount - 1].z
    
        #    wallTop=sRef.properties.wallH
    
        wallTop2 = wallTop * 2
    
        wallDome = settings['Radial']
        wallSlope = settings['Slope']
    
        blockWidth = sRef.properties.blockX
    
        blockGap = sRef.properties.Grout
        halfGrout = blockGap / 2  # half grout for block size modifier
        blockHMin = BLOCK_MIN + blockGap
    
        blockDhalf = settings['d'] / 2  # offset by half block depth to match UI setting
    
        for rowidx in range(rowCount):  # add blocks for each row.
            rows[rowidx].FillBlocks()
    
        if sRef.properties.blockVar and sRef.properties.blockMerge:  # merge (vertical) blocks in close proximity...
            blockSpace = blockGap
            for rowidx in range(rowCount - 1):
                if wallDome:
                    blockSpace = blockGap / (wallTop * sin(abs(rows[rowidx].z) * cPie / wallTop2))
    
                #            else: blockSpace=blockGap/(abs(rows[rowidx].z)) # make it flat
    
    
                idxThis = len(rows[rowidx].BlocksNorm[:]) - 1
                idxThat = len(rows[rowidx + 1].BlocksNorm[:]) - 1
    
                while True:
                    # end loop when either array idx wraps
                    if idxThis < 0 or idxThat < 0:
                        break
    
                    blockThis = rows[rowidx].BlocksNorm[idxThis]
                    blockThat = rows[rowidx + 1].BlocksNorm[idxThat]
    
                    cx, cz, cw, ch, cd = blockThis[:5]
                    ox, oz, ow, oh, od = blockThat[:5]
    
                    if (abs(cw - ow) < blockSpace) and (abs(cx - ox) < blockSpace):
                        if cw > ow:
                            BlockW = ow
                        else:
                            BlockW = cw
    
                        AllBlocks.append([(cx + ox) / 2, (cz + oz + (oh - ch) / 2) / 2, BlockW, abs(cz - oz) + (ch + oh) / 2, (cd + od) / 2, None])
    
                        rows[rowidx].BlocksNorm.pop(idxThis)
                        rows[rowidx + 1].BlocksNorm.pop(idxThat)
                        idxThis -= 1
                        idxThat -= 1
    
                    elif cx > ox:
                        idxThis -= 1
                    else:
                        idxThat -= 1
    
    
        ####
        # Add blocks to create a "shelf/platform".
        # Does not account for openings (crosses gaps - which is a good thing)
    
        if settings['Shelf']:
    
            # Use wall block settings for shelf
            shelfBW = blockWidth
            shelfBWVar = settings['wv']
            shelfBH = sRef.properties.blockZ
    
            ShelfLft = sRef.properties.ShelfX
            ShelfBtm = sRef.properties.ShelfZ
            ShelfRt = ShelfLft + sRef.properties.ShelfW
            ShelfTop = ShelfBtm + sRef.properties.ShelfH
            ShelfThk = sRef.properties.ShelfD
            ShelfThk2 = ShelfThk * 2  # double-depth to position at cursor.
    
            if sRef.properties.ShelfOut:  # place blocks on outside of wall
                ShelfOffsets = [[0, -blockDhalf, 0], [0, -ShelfThk, 0], [0, -blockDhalf, 0], [0, -ShelfThk, 0], [0, -blockDhalf, 0], [0, -ShelfThk, 0], [0, -blockDhalf, 0], [0, -ShelfThk, 0]]
            else:
                ShelfOffsets = [[0, ShelfThk, 0], [0, blockDhalf, 0], [0, ShelfThk, 0], [0, blockDhalf, 0], [0, ShelfThk, 0], [0, blockDhalf, 0], [0, ShelfThk, 0], [0, blockDhalf, 0]]
    
            while ShelfBtm < ShelfTop:  # Add blocks for each "shelf row" in area
                divs = fill(ShelfLft, ShelfRt, shelfBW, shelfBW, shelfBWVar)
    
                for i in range(len(divs) - 1):  # add blocks for row divisions
                    ThisBlockx = (divs[i] + divs[i + 1]) / 2
                    ThisBlockw = divs[i + 1] - divs[i] - halfGrout
                    AllBlocks.append([ThisBlockx, ShelfBtm, ThisBlockw, shelfBH, ShelfThk2, ShelfOffsets])
    
                ShelfBtm += shelfBH + halfGrout  # moving up to next row...
    
    
        # Set shelf material/color... on wish list.
    
        ####
        # Add blocks to create "steps".
        # Does not account for openings (crosses gaps - which is a good thing)
    
        if settings['Steps']:
    
            stepsFill = settings['StepsB']
            steps2Left = settings['StepsL']
    
            # step block "filler" by wall block settings.
            stepFW = blockWidth
            StepFWVar = settings['wv']
    
            StepXMod = sRef.properties.StepT  # step tread, also sets basic block size.
            StepZMod = sRef.properties.StepV
    
            StepLft = sRef.properties.StepX
            StepWide = sRef.properties.StepW
            StepRt = StepLft + StepWide
            StepBtm = sRef.properties.StepZ + StepZMod / 2  # Start offset for centered blocks
            StepTop = StepBtm + sRef.properties.StepH
    
            StepThk = sRef.properties.StepD
            StepThk2 = StepThk * 2  # use double-depth due to offsets to position at cursor.
    
            # Use "corners" to adjust steps so not centered on depth.
            # steps at cursor so no gaps between steps and wall face due to wall block depth.
            if settings['StepsO']:  # place blocks on outside of wall
                StepOffsets = [[0, -blockDhalf, 0], [0, -StepThk, 0], [0, -blockDhalf, 0], [0, -StepThk, 0], [0, -blockDhalf, 0], [0, -StepThk, 0], [0, -blockDhalf, 0], [0, -StepThk, 0]]
            else:
                StepOffsets = [[0, StepThk, 0], [0, blockDhalf, 0], [0, StepThk, 0], [0, blockDhalf, 0], [0, StepThk, 0], [0, blockDhalf, 0], [0, StepThk, 0], [0, blockDhalf, 0]]
    
            # Add steps for each "step row" in area (neg width is interesting but prevented)
            while StepBtm < StepTop and StepWide > 0:
    
                # Make blocks for each step row - based on rowOb:fillblocks
                if stepsFill:
                    divs = fill(StepLft, StepRt, StepXMod, stepFW, StepFWVar)
    
                    # loop through the row divisions, adding blocks for each one
                    for i in range(len(divs) - 1):
                        ThisBlockx = (divs[i] + divs[i + 1]) / 2
                        ThisBlockw = divs[i + 1] - divs[i] - halfGrout
    
                        AllBlocks.append([ThisBlockx, StepBtm, ThisBlockw, StepZMod, StepThk2, StepOffsets])
                else:  # "cantilevered steps"
                    if steps2Left:
                        stepStart = StepRt - StepXMod
                    else:
                        stepStart = StepLft
    
                    AllBlocks.append([stepStart, StepBtm, StepXMod, StepZMod, StepThk2, StepOffsets])
    
                StepBtm += StepZMod + halfGrout  # moving up to next row...
                StepWide -= StepXMod  # reduce step width
    
                # adjust side limit depending on direction of steps
                if steps2Left:
                    StepRt -= StepXMod  # move from right
                else:
                    StepLft += StepXMod  # move from left
    
    
        ####
        # Copy all the blocks out of the rows
    
        for row in rows:
            AllBlocks += row.BlocksEdge
            AllBlocks += row.BlocksNorm
    
    
        ####
        # make individual blocks for each block specified in the plan
    
    2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838
        subDivision = settings['sdv']
    
        for block in AllBlocks:
            x, z, w, h, d, corners = block
            holeW2 = w / 2
    
            geom = MakeABlock([x - holeW2, x + holeW2, z - h / 2, z + h / 2, -d / 2, d / 2], subDivision, len(wallVs), corners)
            wallVs += geom[0]
            wallFs += geom[1]
    
        # make Arches for every opening specified in the plan.
        for hole in holeList:
            # lower arch stones
            if hole.vl > 0 and hole.rtl > blockHMin:
                archGeneration(hole, wallVs, wallFs, -1)
    
            # top arch stones
            if hole.v > 0 and hole.rt > blockHMin:
                archGeneration(hole, wallVs, wallFs, 1)
    
        if wallSlope:  # Curve wall, dome shape if "radialized".
            for i, vert in enumerate(wallVs):
                wallVs[i] = [vert[0], (wallTop + vert[1]) * cos(vert[2] * cPie / wallTop2), (wallTop + vert[1]) * sin(vert[2] * cPie / wallTop2)]
    
        if wallDome:  # Make wall circular, dome if sloped, else disc (flat round).
            for i, vert in enumerate(wallVs):
                wallVs[i] = [vert[2] * cos(vert[0]), vert[2] * sin(vert[0]), vert[1]]
    
        return wallVs, wallFs
    
    
    ################################################################################
    #
    # create a list of openings from the general specifications.
    #
    def openList(sRef):
        boundlist = []
    
        # initialize variables
        areaStart = dims['s']
        areaEnd = dims['e']
    
        SetWid = sRef.properties.blockX
        wallDisc = settings['Radial']
    
        for x in openingSpecs:
            if x['a']:  # apply opening to object
                # hope this is faster, at least for repeat.
                xOpenW = x['w']
                xOpenX = x['x']
                xOpenZ = x['z']
    
                if x['n']:  # repeat...
                    if wallDisc:
                        r1 = xOpenZ
                    else:
                        r1 = 1
    
                    if xOpenX > (xOpenW + SetWid):
                        spacing = xOpenX / r1
                    else:
                        spacing = (xOpenW + SetWid) / r1
    
                    minspacing = (xOpenW + SetWid) / r1
    
                    divs = fill(areaStart, areaEnd, spacing, minspacing, center=1)
    
                    for posidx in range(len(divs) - 2):
                        boundlist.append(opening(divs[posidx + 1], xOpenZ, xOpenW, x['h'], x['v'], x['t'], x['vl'], x['tl'], x['bvl']))
    
                else:
                    boundlist.append(opening(xOpenX, xOpenZ, xOpenW, x['h'], x['v'], x['t'], x['vl'], x['tl'], x['bvl']))
                # check for edge overlap?
    
        return boundlist
    
    
    ################################################################################
    #
    # fill a linear space with divisions
    #
    #    objXO: x origin
    #    objXL: x limit
    #    avedst: the average distance between points
    #    mindst: the minimum distance between points
    #    dev: the maximum random deviation from avedst
    #    center: flag to center the elements in the range, 0 == disabled
    #
    # returns an ordered list of points, including the end points.
    #
    def fill(objXO, objXL, avedst, mindst=0.0, dev=0.0, center=0):
    
        curpos = objXO
        poslist = [curpos]  # set starting point
    
        # Set offset by average spacing, then add blocks (fall through);
        # if not at edge.
        if center:
            curpos += ((objXL - objXO - mindst * 2) % avedst) / 2 + mindst
            if curpos - poslist[-1] < mindst:
                curpos = poslist[-1] + mindst + random() * dev / 2
    
            # clip to right edge.
            if (objXL - curpos < mindst) or (objXL - curpos < mindst):
                poslist.append(objXL)
                return poslist
            else:
                poslist.append(curpos)
    
        # make block edges
        while True:
            curpos += avedst + rndd() * dev
            if curpos - poslist[-1] < mindst:
                curpos = poslist[-1] + mindst + random() * dev / 2
    
            if (objXL - curpos < mindst) or (objXL - curpos < mindst):
                poslist.append(objXL)  # close off edges at limit
                return poslist
            else:
                poslist.append(curpos)
    
    
    #######################################################################
    #
    # MakeABlock: Generate block geometry
    #  to be made into a square cornered block, subdivided along the length.
    #
    #  bounds: a list of boundary positions:
    #      0:left, 1:right, 2:bottom, 3:top, 4:front, 5:back
    #  segsize: the maximum size before subdivision occurs
    #  vll: the number of vertexes already in the mesh. len(mesh.verts) should
    #          give this number.
    #  Offsets: list of coordinate delta values.
    #      Offsets are lists, [x,y,z] in
    #          [
    #          0:left_bottom_back,
    #          1:left_bottom_front,
    #          2:left_top_back,
    #          3:left_top_front,
    #          4:right_bottom_back,
    #          5:right_bottom_front,
    #          6:right_top_back,
    #          7:right_top_front,
    #          ]
    #  FaceExclude: list of faces to exclude from the faces list; see bounds above for indices
    #
    #  return lists of points and faces.
    #
    def MakeABlock(bounds, segsize, vll=0, Offsets=None, FaceExclude=[]):
    
        slices = fill(bounds[0], bounds[1], segsize, segsize, center=1)
        points = []
        faces = []
    
        if Offsets == None:
            points.append([slices[0], bounds[4], bounds[2]])
            points.append([slices[0], bounds[5], bounds[2]])
            points.append([slices[0], bounds[5], bounds[3]])
            points.append([slices[0], bounds[4], bounds[3]])
    
            for x in slices[1:-1]:
                points.append([x, bounds[4], bounds[2]])
                points.append([x, bounds[5], bounds[2]])
                points.append([x, bounds[5], bounds[3]])
                points.append([x, bounds[4], bounds[3]])
    
            points.append([slices[-1], bounds[4], bounds[2]])
            points.append([slices[-1], bounds[5], bounds[2]])
            points.append([slices[-1], bounds[5], bounds[3]])
            points.append([slices[-1], bounds[4], bounds[3]])
    
        else:
            points.append([slices[0] + Offsets[0][0], bounds[4] + Offsets[0][1], bounds[2] + Offsets[0][2]])
            points.append([slices[0] + Offsets[1][0], bounds[5] + Offsets[1][1], bounds[2] + Offsets[1][2]])
            points.append([slices[0] + Offsets[3][0], bounds[5] + Offsets[3][1], bounds[3] + Offsets[3][2]])
            points.append([slices[0] + Offsets[2][0], bounds[4] + Offsets[2][1], bounds[3] + Offsets[2][2]])
    
            for x in slices[1:-1]:
                xwt = (x - bounds[0]) / (bounds[1] - bounds[0])
                points.append([x + Offsets[0][0] * (1 - xwt) + Offsets[4][0] * xwt, bounds[4] + Offsets[0][1] * (1 - xwt) + Offsets[4][1] * xwt, bounds[2] + Offsets[0][2] * (1 - xwt) + Offsets[4][2] * xwt])
                points.append([x + Offsets[1][0] * (1 - xwt) + Offsets[5][0] * xwt, bounds[5] + Offsets[1][1] * (1 - xwt) + Offsets[5][1] * xwt, bounds[2] + Offsets[1][2] * (1 - xwt) + Offsets[5][2] * xwt])
                points.append([x + Offsets[3][0] * (1 - xwt) + Offsets[7][0] * xwt, bounds[5] + Offsets[3][1] * (1 - xwt) + Offsets[7][1] * xwt, bounds[3] + Offsets[3][2] * (1 - xwt) + Offsets[7][2] * xwt])
                points.append([x + Offsets[2][0] * (1 - xwt) + Offsets[6][0] * xwt, bounds[4] + Offsets[2][1] * (1 - xwt) + Offsets[6][1] * xwt, bounds[3] + Offsets[2][2] * (1 - xwt) + Offsets[6][2] * xwt])
    
            points.append([slices[-1] + Offsets[4][0], bounds[4] + Offsets[4][1], bounds[2] + Offsets[4][2]])
            points.append([slices[-1] + Offsets[5][0], bounds[5] + Offsets[5][1], bounds[2] + Offsets[5][2]])
            points.append([slices[-1] + Offsets[7][0], bounds[5] + Offsets[7][1], bounds[3] + Offsets[7][2]])
            points.append([slices[-1] + Offsets[6][0], bounds[4] + Offsets[6][1], bounds[3] + Offsets[6][2]])
    
        faces.append([vll, vll + 3, vll + 2, vll + 1])
    
        for x in range(len(slices) - 1):
            faces.append([vll, vll + 1, vll + 5, vll + 4])
            vll += 1
            faces.append([vll, vll + 1, vll + 5, vll + 4])
            vll += 1
            faces.append([vll, vll + 1, vll + 5, vll + 4])
            vll += 1
            faces.append([vll, vll - 3, vll + 1, vll + 4])
            vll += 1
    
        faces.append([vll, vll + 1, vll + 2, vll + 3])
    
        return points, faces
    
    
    #
    # For generating Keystone Geometry
    def MakeAKeystone(xpos, width, zpos, ztop, zbtm, thick, bevel, vll=0, FaceExclude=[], xBevScl=1):
        __doc__ = """\
        MakeAKeystone returns lists of points and faces to be made into a square cornered keystone, with optional bevels.
        xpos: x position of the centerline
        width: x width of the keystone at the widest point (discounting bevels)
        zpos: z position of the widest point
        ztop: distance from zpos to the top
        zbtm: distance from zpos to the bottom
        thick: thickness
        bevel: the amount to raise the back vertex to account for arch beveling
        vll: the number of vertexes already in the mesh. len(mesh.verts) should give this number
        faceExclude: list of faces to exclude from the faces list.  0:left, 1:right, 2:bottom, 3:top, 4:back, 5:front
        xBevScl: how much to divide the end (+- x axis) bevel dimensions.  Set to current average radius to compensate for angular distortion on curved blocks
        """
    
        points = []
        faces = []
        faceinclude = [1 for x in range(6)]
        for x in FaceExclude:
            faceinclude[x] = 0
        Top = zpos + ztop
        Btm = zpos - zbtm
        Wid = width / 2.
        Thk = thick / 2.
    
        # The front top point
        points.append([xpos, Thk, Top])
        # The front left point
        points.append([xpos - Wid, Thk, zpos])
        # The front bottom point
        points.append([xpos, Thk, Btm])
        # The front right point
        points.append([xpos + Wid, Thk, zpos])
    
        MirrorPoints = []
        for i in points:
            MirrorPoints.append([i[0], -i[1], i[2]])
        points += MirrorPoints
        points[6][2] += bevel
    
        faces.append([3, 2, 1, 0])
        faces.append([4, 5, 6, 7])
        faces.append([4, 7, 3, 0])
        faces.append([5, 4, 0, 1])
        faces.append([6, 5, 1, 2])
        faces.append([7, 6, 2, 3])
        # Offset the vertex numbers by the number of verticies already in the list
        for i in range(len(faces)):
            for j in range(len(faces[i])):
                faces[i][j] += vll
    
        return points, faces
    
    
    # class openings in the wall
    class opening:
        __doc__ = """\
        This is the class for holding the data for the openings in the wall.
        It has methods for returning the edges of the opening for any given position value,
        as well as bevel settings and top and bottom positions.
        It stores the 'style' of the opening, and all other pertinent information.
        """
        # x = 0. # x position of the opening
        # z = 0. # x position of the opening
        # w = 0. # width of the opening
        # h = 0. # height of the opening
        r = 0  # top radius of the arch (derived from 'v')
        rl = 0  # lower radius of the arch (derived from 'vl')
        rt = 0  # top arch thickness
        rtl = 0  # lower arch thickness
        ts = 0  # Opening side thickness, if greater than average width, replaces it.
        c = 0  # top arch corner position (for low arches), distance from the top of the straight sides
        cl = 0  # lower arch corner position (for low arches), distance from the top of the straight sides
        # form = 0 # arch type (unused for now)
        # b = 0. # back face bevel distance, like an arrow slit
        v = 0.  # top arch height
        vl = 0.  # lower arch height
        # variable "s" is used for "side" in the "edge" function.
        # it is a signed int, multiplied by the width to get + or - of the center
    
        def btm(self):
            if self.vl <= self.w / 2:
                return self.z - self.h / 2 - self.vl - self.rtl
            else:
                return self.z - sqrt((self.rl + self.rtl)**2 - (self.rl - self.w / 2)**2) - self.h / 2
    
        def top(self):
            if self.v <= self.w / 2:
                return self.z + self.h / 2 + self.v + self.rt
            else:
                return sqrt((self.r + self.rt)**2 - (self.r - self.w / 2)**2) + self.z + self.h / 2
    
        # crits returns the critical split points, or discontinuities, used for making rows
        def crits(self):
            critlist = []
            if self.vl > 0:  # for lower arch
                # add the top point if it is pointed
                #if self.vl >= self.w/2.: critlist.append(self.btm())
                if self.vl < self.w / 2.:  # else: for low arches, with wedge blocks under them
                    # critlist.append(self.btm())
                    critlist.append(self.z - self.h / 2 - self.cl)
    
            if self.h > 0:  # if it has a height, append points at the top and bottom of the main square section
                critlist += [self.z - self.h / 2, self.z + self.h / 2]
            else:  # otherwise, append just one in the center
                critlist.append(self.z)
    
            if self.v > 0:  # for the upper arch
                if self.v < self.w / 2.:  # add the splits for the upper wedge blocks, if needed
                    critlist.append(self.z + self.h / 2 + self.c)
                    # critlist.append(self.top())
                # otherwise just add the top point, if it is pointed
                # else: critlist.append(self.top())
    
            return critlist
    
        #
        # get the side position of the opening.
        # ht is the z position; s is the side: 1 for right, -1 for left
        # if the height passed is above or below the opening, return None
        #
        def edgeS(edgeParms, ht, s):
    
            wallTopZ = dims['t']
            wallHalfH = edgeParms.h / 2
            wallHalfW = edgeParms.w / 2
            wallBase = edgeParms.z
    
            # set the row radius: 1 for standard wall (flat)
            if settings['Radial']:
                if settings['Slope']:
                    r1 = abs(wallTopZ * sin(ht * cPie / (wallTopZ * 2)))
                else:
                    r1 = abs(ht)
            else:
                r1 = 1
    
            # Go through all the options, and return the correct value
            if ht < edgeParms.btm():  # too low
                return None
            elif ht > edgeParms.top():  # too high
                return None
    
            # in this range, pass the lower arch info
            elif ht <= wallBase - wallHalfH - edgeParms.cl:
                if edgeParms.vl > wallHalfW:
                    circVal = circ(ht - wallBase + wallHalfH, edgeParms.rl + edgeParms.rtl)
                    if circVal == None:
                        return None
                    else:
                        return edgeParms.x + s * (wallHalfW - edgeParms.rl + circVal) / r1
                else:
                    circVal = circ(ht - wallBase + wallHalfH + edgeParms.vl - edgeParms.rl, edgeParms.rl + edgeParms.rtl)
                    if circVal == None:
                        return None
                    else:
                        return edgeParms.x + s * circVal / r1
    
            # in this range, pass the top arch info
            elif ht >= wallBase + wallHalfH + edgeParms.c:
                if edgeParms.v > wallHalfW:
                    circVal = circ(ht - wallBase - wallHalfH, edgeParms.r + edgeParms.rt)
                    if circVal == None:
                        return None
                    else:
                        return edgeParms.x + s * (wallHalfW - edgeParms.r + circVal) / r1
                else:
                    circVal = circ(ht - (wallBase + wallHalfH + edgeParms.v - edgeParms.r), edgeParms.r + edgeParms.rt)
                    if circVal == None:
                        return None
                    else:
                        return edgeParms.x + s * circVal / r1
    
            # in this range pass the lower corner edge info
            elif ht <= wallBase - wallHalfH:
                d = sqrt(edgeParms.rtl**2 - edgeParms.cl**2)
                if edgeParms.cl > edgeParms.rtl / sqrt(2.):
                    return edgeParms.x + s * (wallHalfW + (wallBase - wallHalfH - ht) * d / edgeParms.cl) / r1
                else:
                    return edgeParms.x + s * (wallHalfW + d) / r1
    
            # in this range pass the upper corner edge info
            elif ht >= wallBase + wallHalfH:
                d = sqrt(edgeParms.rt**2 - edgeParms.c**2)
                if edgeParms.c > edgeParms.rt / sqrt(2.):
                    return edgeParms.x + s * (wallHalfW + (ht - wallBase - wallHalfH) * d / edgeParms.c) / r1
                else:
                    return edgeParms.x + s * (wallHalfW + d) / r1
    
            # in this range, pass the middle info (straight sides)
            else:
                return edgeParms.x + s * wallHalfW / r1
    
        # get the top or bottom of the opening
        # ht is the x position; archSide: 1 for top, -1 for bottom
        #
        def edgeV(self, ht, archSide):
            wallTopZ = dims['t']
            dist = abs(self.x - ht)
    
            def radialAdjust(dist, sideVal):  # adjust distance and for radial geometry.
                if settings['Radial']:
                    if settings['Slope']:
                        dist = dist * abs(wallTopZ * sin(sideVal * cPie / (wallTopZ * 2)))
                    else:
                        dist = dist * sideVal
                return dist
    
            if archSide > 0:  # check top down
                # hack for radialized masonry, import approx Z instead of self.top()
                dist = radialAdjust(dist, self.top())
    
                # no arch on top, flat
                if not self.r:
                    return self.z + self.h / 2
    
                # pointed arch on top
                elif self.v > self.w / 2:
                    circVal = circ(dist - self.w / 2 + self.r, self.r + self.rt)
                    if circVal == None:
                        return 0.0
                    else:
                        return self.z + self.h / 2 + circVal
    
                # domed arch on top
                else:
                    circVal = circ(dist, self.r + self.rt)
                    if circVal == None:
                        return 0.0
                    else:
                        return self.z + self.h / 2 + self.v - self.r + circVal
    
            else:  # check bottom up
                # hack for radialized masonry, import approx Z instead of self.top()
                dist = radialAdjust(dist, self.btm())
    
                # no arch on bottom
                if not self.rl:
                    return self.z - self.h / 2
    
                # pointed arch on bottom
                elif self.vl > self.w / 2:
                    circVal = circ(dist - self.w / 2 + self.rl, self.rl + self.rtl)
                    if circVal == None:
                        return 0.0
                    else:
                        return self.z - self.h / 2 - circVal
    
                # old conditional? if (dist-self.w/2+self.rl)<=(self.rl+self.rtl):
                # domed arch on bottom
                else:
                    circVal = circ(dist, self.rl + self.rtl)  # dist-self.w/2+self.rl
                    if circVal == None:
                        return 0.0
                    else:
                        return self.z - self.h / 2 - self.vl + self.rl - circVal
    
        #
        def edgeBev(self, ht):
            wallTopZ = dims['t']
            if ht > (self.z + self.h / 2):
                return 0.0
            if ht < (self.z - self.h / 2):
                return 0.0
            if settings['Radial']:
                if settings['Slope']:
                    r1 = abs(wallTopZ * sin(ht * cPie / (wallTopZ * 2)))
                else:
                    r1 = abs(ht)
            else:
                r1 = 1
            bevel = self.b / r1
            return bevel
    
    
        def __init__(self, xpos, zpos, width, height, archHeight=0, archThk=0,
                     archHeightLower=0, archThkLower=0, bevel=0, edgeThk=0):
            self.x = float(xpos)
            self.z = float(zpos)
            self.w = float(width)
            self.h = float(height)
            self.rt = archThk
            self.rtl = archThkLower
            self.v = archHeight
            self.vl = archHeightLower
    
            # find the upper arch radius
            if archHeight >= width / 2:
                # just one arch, low long
                self.r = (self.v**2) / self.w + self.w / 4
            elif archHeight <= 0:
                # No arches
                self.r = 0
                self.v = 0
            else:
                # Two arches
                self.r = (self.w**2) / (8 * self.v) + self.v / 2.
                self.c = self.rt * cos(atan(self.w / (2 * (self.r - self.v))))
    
            # find the lower arch radius
            if archHeightLower >= width / 2:
                self.rl = (self.vl**2) / self.w + self.w / 4
            elif archHeightLower <= 0:
                self.rl = 0
                self.vl = 0
            else:
                self.rl = (self.w**2) / (8 * self.vl) + self.vl / 2.
                self.cl = self.rtl * cos(atan(self.w / (2 * (self.rl - self.vl))))
    
            # self.form = something?
            self.b = float(bevel)
            self.ts = edgeThk
    
    #
    #
    # class for the whole wall boundaries; a sub-class of "opening"
    
    
    class OpeningInv(opening):
        # this is supposed to switch the sides of the opening
        # so the wall will properly enclose the whole wall.
    
        def edgeS(self, ht, s):
            return opening.edgeS(self, ht, -s)
    
        def edgeV(self, ht, s):
            return opening.edgeV(self, ht, -s)
    
    # class rows in the wall
    
    
    class rowOb:
        __doc__ = """\
        This is the class for holding the data for individual rows of blocks.
        each row is required to have some edge blocks, and can also have
        intermediate sections of "normal" blocks.
        """
    
        #z = 0.
        #h = 0.
        radius = 1
        rowEdge = 0
    
        def FillBlocks(self):
            wallTopZ = dims['t']
    
            # Set the radius variable, in the case of radial geometry
            if settings['Radial']:
                if settings['Slope']:
                    self.radius = wallTopZ * (sin(self.z * cPie / (wallTopZ * 2)))
                else:
                    self.radius = self.z
    
            # initialize internal variables from global settings
            SetH = settings['h']
    
            # no HVar?
    
            SetWid = settings['w']
            SetWidVar = settings['wv']
            SetGrt = settings['g']
            SetDepth = settings['d']
            SetDepthVar = settings['dv']
    
            # height weight, make shorter rows have narrower blocks, and vice-versa
            rowHWt = ((self.h / SetH - 1) * ROW_H_WEIGHT + 1)
    
            # set variables for persistent values: loop optimization, readability, single ref for changes.
            avgDist = rowHWt * SetWid / self.radius
            minDist = SetWid / self.radius
            deviation = rowHWt * SetWidVar / self.radius
            grtOffset = SetGrt / (2 * self.radius)
    
            # init loop variables that may change...
            blockGap = SetGrt / self.radius
            ThisBlockHeight = self.h
            ThisBlockDepth = SetDepth + (rndd() * SetDepthVar)
    
            for segment in self.RowSegments:
                divs = fill(segment[0] + grtOffset, segment[1] - grtOffset, avgDist, minDist, deviation)
    
                # loop through the divisions, adding blocks for each one
                for i in range(len(divs) - 1):
                    ThisBlockx = (divs[i] + divs[i + 1]) / 2
                    ThisBlockw = divs[i + 1] - divs[i] - blockGap
    
                    self.BlocksNorm.append([ThisBlockx, self.z, ThisBlockw, ThisBlockHeight, ThisBlockDepth, None])
    
                    if SetDepthVar:  # vary depth
                        ThisBlockDepth = SetDepth + (rndd() * SetDepthVar)
    
        def __init__(self, centerheight, rowheight, rowEdge=0):
            self.z = float(centerheight)
            self.h = float(rowheight)
            self.rowEdge = float(rowEdge)
    
    
            # THIS INITILIZATION IS IMPORTANT!  OTHERWISE ALL OBJECTS WILL HAVE THE SAME LISTS!
    
            self.BlocksEdge = []
            self.RowSegments = []
            self.BlocksNorm = []
    
    #
    
    
    def arch(ra, rt, x, z, archStart, archEnd, bevel, bevAngle, vll):
        __doc__ = """\
        Makes a list of faces and vertexes for arches.
        ra: the radius of the arch, to the center of the bricks
        rt: the thickness of the arch
        x: x center location of the circular arc, as if the arch opening were centered on x = 0
        z: z center location of the arch
        anglebeg: start angle of the arch, in radians, from vertical?
        angleend: end angle of the arch, in radians, from vertical?
        bevel: how much to bevel the inside of the arch.
        vll: how long is the vertex list already?
        """
        avlist = []
        aflist = []
    
        # initialize internal variables for global settings
        SetH = settings['h']
        SetWid = settings['w']
        SetWidVar = settings['wv']
        SetGrt = settings['g']
        SetDepth = settings['d']
        SetDepthVar = settings['dv']
        wallTopZ = dims['t']
    
        wallDome = settings['Radial']
    
        ArchInner = ra - rt / 2
        ArchOuter = ra + rt / 2 - SetGrt
    
        DepthBack = -SetDepth / 2 - rndc() * SetDepthVar