On the matter of adding Bevels automatically

Thoughts, Help, Feature Requests, Bug Reports, Developing code for...

Moderators: dorpond, trevor, Azhrei

Forum rules
PLEASE don't post images of your entire desktop, attach entire campaign files when only a single file is needed, or generally act in some other anti-social behavior. :)
Post Reply
User avatar
wolph42
Winter Wolph
Posts: 9999
Joined: Fri Mar 20, 2009 5:40 am
Location: Netherlands
Contact:

On the matter of adding Bevels automatically

Post by wolph42 »

Open question. I'm looking into a process to add bevels (these are small gradient bars that make a texture look like its rounded) to tiles. I've come up with the idea to 'scan' 9 cells at the time and look from the perspective of the centre cell.
I've put the process in excel as an
example scan
Schermafdruk 2016-05-18 11.30.02.png
Schermafdruk 2016-05-18 11.30.02.png (74.04 KiB) Viewed 988 times
of one room (in yellow). Some notes:
- the bevels are applied to the WALL (so NOT to the yellow cells, which are 'empty space' but to the white cells, which represent walls. From code perspective however I track the rooms not the walls). I've attached the excel as well for those interested.
- the inset picture of bevels helps to get the drift but I've already concluded that using that many different bevels makes it not possible to use this method. So they're just there as a visualisation

What the scan does is simply grab 9 cells and checks them, then move ONE cell to the right until finished with that line and then move ONE cell below and start again. See excel/picture to get the drift.
Note that with 'outer corner' I mean that if you apply a bevel bar

The question: I think I can turn this process into an algorithm but I would appreciate some feedback either on the process, the algortihm, alternatives etc.

Also I'm wondering about the outer corners. Do I need to apply those only as presented in the picture/xls or also with the other corners?

the overall issue I'm struggling with is that there are 512 different configurations possible. (every cell in the 9 square grid can be either wall or space so 2^9=512)
Attachments
Bevels.zip
(22 Bytes) Downloaded 42 times

User avatar
aliasmask
RPTools Team
Posts: 9029
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: On the matter of adding Bevels automatically

Post by aliasmask »

I would use recursion. Start at upper left and then seek to the east and south. If parent is blank and child is room then place texture. Then set child are parent and repeat until you reach the area limit. This will only set the bevels for the North and West of squares, but you can use the same process by checking the North end West children, just never move that way. There are several ways to do this, but most of the work is done in the parent portion of macro.

User avatar
wolph42
Winter Wolph
Posts: 9999
Joined: Fri Mar 20, 2009 5:40 am
Location: Netherlands
Contact:

Re: On the matter of adding Bevels automatically

Post by wolph42 »

aliasmask wrote:I would use recursion. Start at upper left and then seek to the east and south. If parent is blank and child is room then place texture. Then set child are parent and repeat until you reach the area limit. This will only set the bevels for the North and West of squares, but you can use the same process by checking the North end West children, just never move that way. There are several ways to do this, but most of the work is done in the parent portion of macro.
and how to deal with corners?? See this for more info.

User avatar
aliasmask
RPTools Team
Posts: 9029
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: On the matter of adding Bevels automatically

Post by aliasmask »

Something to note is that you will have 256 different bevel image options for 1 square. You could create an image for each or stack them to get the desired effect. Then it would only be 8 images assuming you don't use any transparencies for the beveled part (like shadows).

Code: Select all

Bevels
00000000 = N,NE,E,SE,S,SW,W,NW
Depending on how you store your map, you can just loop though the map looking at the empty spaces and then look at the surrounding spaces to determine which bevels go in that square. For example:

Code: Select all

BBBBBBB
BEEEEEB
BERREEB
BERRREB
BEERREB
BEEEEEB
BBBBBBB
2,4 is 3 across 5 down and has an E for empty. Looking around that square you find 11100000, so you can either put down that 1 image which is a NE inside corner or you can put down 3 images which is North+East+NEcorner. Depending on the size of your maps it may be more efficient to actually have 256 images.

User avatar
wolph42
Winter Wolph
Posts: 9999
Joined: Fri Mar 20, 2009 5:40 am
Location: Netherlands
Contact:

Re: On the matter of adding Bevels automatically

Post by wolph42 »

I want to go with one type of bevel instead of two. (so only the dark ones, not the white). That way there are only 4 images required. Choosing between 4 and 256 Ill have to go with 4 as the 256 really is not feasible.

For determination I have the same method as you give above except I start with E and counterclockwise (don't ask why, legacy from DB2). So the map is initially stored as {x:,y:} coords for the rooms (empty spaces) and then those are converted to e.g. t110110110 (lets call these 'tbinaries' and the individual 1/0 'binaries')

I actually use 9 binaries as I scan the area in 3x3 squares where the last digit is the centre so:
E,NE,N,NW,W,SW,S,SE,C(entre). From there I check the surrounding around the centre (and thus is it important to know whether the centre is space or wall)
I *could* possibly ONLY check the cells that have a coordinate to limit the loops instead of looping through the entire field (as shown in the excel image) but thats optimization. First I want to scan through the entire map to see where that gets me.

I would like to use this method as I already have to make this conversion to check the map for doors. (which I already finished and it works!!). Later on I can also use this method to decide where e.g. beds, closets, chests, tables etc come.

edit3: did some tests and its MUCH faster to use the tbinaries of the "space-cells" (so 3x3 area with a space in the centre) then it is of the "wall-cells" (dito but wall in teh centre). So the scanning needs to be done from the perspective of the space-cells.

edit1: it turns out that its possible to do it with 3 images see this post: http://forums.rptools.net/viewtopic.php ... 95#p263195

edit2: and back to four. The horizontal edge bezel is a 1x2 which you cannot rotate to 2x1. So you need a rotated version of that as well for the vertical edges.

QUESTION:So were back to the main question: HOW do I program this, in terms of algorithm is good enough for me. I'm still breaking my head over this unfortunately. And this question boils down to two sub questions: how to detect the different bezel requirements and WHEN to apply them. Have a look at this excel sheet. I've chosen to place the outer and inner corners when first encountered, but this seems completely arbitrary. So 'when' do I place such a corner and when not??
here my proof of concept images:
Image
Image
Image
and here with background so you can see how they fit in a tile:
1x2
Image
1x1
Image
2x2
Image

User avatar
wolph42
Winter Wolph
Posts: 9999
Joined: Fri Mar 20, 2009 5:40 am
Location: Netherlands
Contact:

Re: On the matter of adding Bevels automatically

Post by wolph42 »

ok I figured out one method. After converting to txxxxxxxxx (tBinaries), I can filter out only the tiles that have a space in the centre and then per tBinary scan I can use regex to change it in a strprop to very swiftly convert the tBinary into separate variables:

Code: Select all

    <!-- turn tile into separate variables -->
    [varsFromStrProp(replace(
        tile,
        "t(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)",
        "E=\$1;NE=\$2;N=\$3;NW=\$4;W=\$5;SW=\$6;S=\$7;SE=\$8;"
    ))]
 
these I can use to reassemble them into 3 adjacent coordinates which tells me what should happen on that side (as I know that the cell i'm looking from is empty) e.g.:

Code: Select all

    <!-- EAST EDGE CHECK -->
    [switch("t"+SE+E+NE), CODE:
        case "t000":{
            <!-- EV= vertical edge -->
            [copyToken("bevelEV",1,"",json.set(coord,"useDistance",0))]
        ''
        };
<!-- In this example the east side of the empty cell is checked (0=wall, 1=space), since all three cells are wall it means that a vertical edge needs to be placed to the east side of this cell. -->
 
full test code for those interested

Code: Select all

[h:'<!-- ------------------------------------ addBevel() ------------------------------------------------- -->']
[h:dungeon    = getLibProperty("DB3.Dungeon","lib:DB3Test"]

[h,foreach(tile, Field), CODE:{
    <!-- create x and y coord of current Centre -->
    [coord    = jons.get(dungeon, roll.count)]
    [varsFromStrProp(json.toStrProp(coord))]
    
    <!-- turn tile into separate variables -->
    [varsFromStrProp(replace(
        tile,
        "t(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)",
        "E=\$1;NE=\$2;N=\$3;NW=\$4;W=\$5;SW=\$6;S=\$7;SE=\$8;"
    ))]

    <!-- EAST EDGE CHECK -->
    [switch("t"+SE+E+NE), CODE:
        case "t000":{
            <!-- EV -->
            [copyToken("bevelEV",1,"",json.set(coord,"useDistance",0))]
        ''
        };
        case "t001":{
            <!-- EV CO(S) -->
            [copyToken("bevelEV",1,"",json.set(coord,"useDistance",0))]
            [copyToken("bevelCO",1,"",json.set(coord,"useDistance",0,facing,-90))]
        ''
        };
        case "t010":{
            <!-- EV CO(N) -->
            [copyToken("bevelEV",1,"",json.set(coord,"useDistance",0))]
            [copyToken("bevelCO",1,"",json.set(coord, "y", json.get(coord,"y")-1,"useDistance",0,facing,0))]
        ''
        };
        case "t011":{
            <!-- EV CO CO -->
            [copyToken("bevelEV",1,"",json.set(coord,"useDistance",0))]
            [copyToken("bevelCO",1,"",json.set(coord, ,"useDistance",0,facing,-90))]
            [copyToken("bevelCO",1,"",json.set(coord, "y", json.get(coord,"y")-1,"useDistance",0,facing,0))]
        ''
        };
        default{<!-- ignore -->}
    ]

    <!-- NORTH EAST CORNER CHECK --><!-- IC ONLY --> 
    [h,if(NE+N+NW == 0): copyToken("bevelCO",1,"",json.set(coord, ,"useDistance",0,facing,??))]

    <!-- OTHER SIDES TO BE DONE LATER AFTER THOROUGHT TESTING -->
    [r:"t"+N+NW+W]
    [r:"t"+NW+W+SW]
    [r:"t"+W+SW+S]
    [r:"t"+SW+S+SE]
    [r:"t"+S+SE+E]
}] 
this won't be fast as I have to scan 8 'sides' per empty cell in the entire dungeon (in a typical 30x30=900 there are roughly 250 cells to scan x8 = 2000 checks in a 30x30 dungeon and thus 8000 in a 60x60 which is my goal.

I can add some extra filters e.g.
- t111111111 is of no interest as there are no walls
- t010101010 is of no interest as only the corners are walls, not one of the cardinal (N,E,S,W) edges.

Any suggestions are still most welcome!

User avatar
aliasmask
RPTools Team
Posts: 9029
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: On the matter of adding Bevels automatically

Post by aliasmask »

Instead of using a string you could just use a number and use the bitwise functions. Each position represents a number, ie 1,2,4,8,16,32,64,128. So, if you want to check the number for a certain position like 16 then band(num,16) where if true then value won't be 0. You can also check for multiple by combining values like 16 and 8, band(num,24) where if true the value returned is 24.

User avatar
wolph42
Winter Wolph
Posts: 9999
Joined: Fri Mar 20, 2009 5:40 am
Location: Netherlands
Contact:

Re: On the matter of adding Bevels automatically

Post by wolph42 »

I've been mulling this over and was wondering what exactly is the advantage? Is it just speed (numbers over strings) or does it have additional advantages?

User avatar
aliasmask
RPTools Team
Posts: 9029
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: On the matter of adding Bevels automatically

Post by aliasmask »

Speed, memory and functions dedicated to bit operations. As you know, things can be done several ways but they all get the job done. I guess whatever you're more comfortable with doing in the end.

User avatar
wolph42
Winter Wolph
Posts: 9999
Joined: Fri Mar 20, 2009 5:40 am
Location: Netherlands
Contact:

Re: On the matter of adding Bevels automatically

Post by wolph42 »

aliasmask wrote:Speed, memory and functions dedicated to bit operations. As you know, things can be done several ways but they all get the job done. I guess whatever you're more comfortable with doing in the end.
thnx. ill first try to get it working and then I can have a look at optimization.

User avatar
wolph42
Winter Wolph
Posts: 9999
Joined: Fri Mar 20, 2009 5:40 am
Location: Netherlands
Contact:

Re: On the matter of adding Bevels automatically

Post by wolph42 »

for the sake of completeness, here the full code:

Below
dungeon is [{"x":1,"y":1},{"x":1,"y":2},{"x":1,"y":3},etc. ] where each coord represents SPACE and
Field is ["t100000111","t111000111","t111000111", etc.] where 1=SPACE and 0=WALL and tx is "tE,NE,N,NW,W,SW,S,SE,Centre"
also the dungeon and field are 'aligned' that is the first tx is from the perspective from the first coord of dungeon, the 2nd with the 2nd, etc. (thus logically "centre" in tx is thus ALWAYS "1")

Code: Select all

[h:'<!-- ------------------------------------ addBevel() ------------------------------------------------- -->']
[debug=0]
[h:dungeon	= getLibProperty("DB3.dungeon","lib:DB3Test")]
[h:Field	= getLibProperty("DB3.tField","lib:DB3Test")]

[h,foreach(tile, Field), CODE:{
	<!-- create x and y coord of current Centre -->
	[coord	= json.get(dungeon, roll.count)]
	[varsFromStrProp(json.toStrProp(coord))]
	
	<!-- turn tile into separate variables -->
	[varsFromStrProp(replace(
		tile,
		"t(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)(0|1)",
		"E=\$1;NE=\$2;N=\$3;NW=\$4;W=\$5;SW=\$6;S=\$7;SE=\$8;"
	))]

	<!-- EAST EDGE CHECK -->
	[if(debug), CODE:{[position="EAST"][tmp	= "t"+SE+E+NE]}]
	[switch("t"+SE+E+NE), CODE:
		case "t000":{
			<!-- EV -->
			[copyToken("BevelEV",1,"",json.set(coord, "useDistance",0,"facing",90))]
		[if(debug):pause("coord", "tile","tmp","position")]
		};
		case "t100": {
			<!-- EV CO(S) -->
			[copyToken("BevelEV",1,"",json.set(coord, "useDistance",0,"facing",90))]
			[copyToken("BevelOC",1,"",json.set(coord, "useDistance",0,"facing",0))]
		[if(debug):pause("coord", "tile","tmp","position")]
		};
		case "t001": {
			<!-- EV CO(N) -->
			[copyToken("BevelEV",1,"",json.set(coord, "useDistance",0,"facing",90))]
			[copyToken("BevelOC",1,"",json.set(coord, "y", json.get(coord,"y")-1, "useDistance",0,"facing",-90))]
		[if(debug):pause("coord", "tile","tmp","position")]
		};
		case "t101": {
			<!-- EV CO CO -->
			[copyToken("BevelEV",1,"",json.set(coord, "useDistance", 0, "facing", 90))]
			[copyToken("BevelOC",1,"",json.set(coord, "y", json.get(coord,"y")-1, "useDistance",0,"facing",-90))]
			[copyToken("BevelOC",1,"",json.set(coord, "useDistance",0,"facing",0))]
		[if(debug):pause("coord", "tile","tmp","position")]
		};
		default: {<!-- ignore -->
	}]

	<!-- NORTH EAST CORNER CHECK --><!-- IC ONLY --> 
	[if(debug), CODE:{[position="NORTH EAST CORNER"][tmp	= "t"+E+NE+N]}]
	[if(E+NE+N == 0), CODE:{
		[copyToken("BevelIC",1,"",json.set(coord, "x", json.get(coord,"x")+1, "y", json.get(coord,"y")-1, "useDistance",0,"facing",180))]
		[if(debug):pause("coord", "tile","tmp","position")]
	}]

	<!-- NORTH EDGE CHECK Do not add OC as this is already done in East and West, hence only place vertical bevel is south = wall = 0 = !1 = !S	-->
	[if(!N): copyToken("BevelEH",1,"",json.set(coord, "y", json.get(coord,"y")-1, "useDistance", 0, "facing", -90))]
	[if(debug), CODE:{[position="NORTH"][tmp	= "t"+NE+N+NW]pause("coord", "tile","tmp","position")}]
	
	<!-- NORTH WEST CORNER CHECK --><!-- IC ONLY --> 
	[if(debug), CODE:{[position="NORTH WEST CORNER"][tmp	= "t"+N+NW+W]}]
	[if(N+NW+W == 0), CODE:{
		[tok=copyToken("BevelIC",1,"",json.set(coord, "x", json.get(coord,"x")-1, "y", json.get(coord,"y")-1, "useDistance",0,"facing",-90))]
		[if(debug):pause("coord", "tile","tmp","position","tok")]
	}]

	<!-- WEST EDGE CHECK -->
	[if(debug), CODE:{[position="WEST"][tmp	= "t"+NW+W+SW]}]
	[switch("t"+NW+W+SW), CODE:
		case "t000":{
			<!-- EV -->
			[copyToken("BevelEV",1,"",json.set(coord, "x", json.get(coord,"x")-1, "useDistance", 0, "facing", -90))]
		[if(debug):pause("coord", "tile","tmp","position")]
		};
		case "t001": {
			<!-- EV CO(SW) -->
			[copyToken("BevelEV",1,"",json.set(coord, "x", json.get(coord,"x")-1, "useDistance", 0, "facing", -90))]
			[copyToken("BevelOC",1,"",json.set(coord, "x", json.get(coord,"x")-1, "useDistance", 0, "facing", 90))]
		[if(debug):pause("coord", "tile","tmp","position")]
		};
		case "t100": {
			<!-- EV CO(NW) -->
			[copyToken("BevelEV",1,"",json.set(coord, "x", json.get(coord,"x")-1, "useDistance", 0, "facing", -90))]
			[copyToken("BevelOC",1,"",json.set(coord, "x", json.get(coord,"x")-1, "y", json.get(coord,"y")-1, "useDistance",0,"facing",180))]
		[if(debug):pause("coord", "tile","tmp","position")]
		};
		case "t101": {
			<!-- EV CO CO -->
			[copyToken("BevelEV",1,"",json.set(coord, "x", json.get(coord,"x")-1, "useDistance", 0, "facing", -90))]
			[copyToken("BevelOC",1,"",json.set(coord, "x", json.get(coord,"x")-1, "useDistance", 0, "facing", 90))]
			[copyToken("BevelOC",1,"",json.set(coord, "x", json.get(coord,"x")-1, "y", json.get(coord,"y")-1, "useDistance",0,"facing",180))]
		[if(debug):pause("coord", "tile","tmp","position")]
		};
		default: {<!-- ignore -->
	}]

	<!-- SOUTH WEST CORNER CHECK --><!-- IC ONLY --> 
	[if(debug), CODE:{[position="SOUTH WEST CORNER"][tmp	= "t"+W+SW+S]}]
	[if(W+SW+S == 0), CODE:{
		[copyToken("BevelIC",1,"",json.set(coord, "x", json.get(coord,"x")-1, "y", json.get(coord,"y")+1, "useDistance",0,"facing",0))]
		[if(debug):pause("coord", "tile","tmp","position")]
	}]

	<!-- SOUTH EDGE CHECK Do not add OC as this is already done in East and West, hence only place vertical bevel is south = wall = 0 = !1 = !S	-->
	[if(!S): copyToken("BevelEH",1,"",json.set(coord, "useDistance", 0, "facing", 90))]
	[if(debug), CODE:{[position="SOUTH"][tmp	= "t"+SW+S+SE]pause("coord", "tile","tmp","position")}]

	<!-- SOUTH EAST CORNER CHECK --><!-- IC ONLY --> 
	[if(debug), CODE:{[position=""][tmp	= "t"+S+SE+E]}]
	[if(S+SE+E == 0), CODE:{
		[copyToken("BevelIC",1,"",json.set(coord, "x", json.get(coord,"x")+1, "y", json.get(coord,"y")+1, "useDistance",0,"facing",90))]
		[if(debug):pause("coord", "tile","tmp","position")]
	}]

}]
o, and it works!!

User avatar
wolph42
Winter Wolph
Posts: 9999
Joined: Fri Mar 20, 2009 5:40 am
Location: Netherlands
Contact:

Re: On the matter of adding Bevels automatically

Post by wolph42 »

I've contemplated I the binary comparison solution you gave alias, but the code add is was pretty hard to debug, adding that layer of complexity will make it unreadable and very hard to change later on

Post Reply

Return to “MapTool”