Docking Ranges and Conditions - roomle

I have a part that is a rectangle of 1200mm x 400mm x 400mm, this part can be divided into 3 equal sizes, within these equal sizes I need to Dock different parts. I have set a Docking Range for every 400mm (which is fine) I have 3 style of parts that need to Dock into the 400mm gaps, so I have set on the main 1200 part 3 KEYS (bridgePos1InfillType, bridgePos2InfillType, bridgePos3InfillType) and currently when I dock a part inside these I set the TYPE that was docked...
"mask": "cubby",
"position": "{0, 0, 40}",
"stepEnd": "{800, 0, 0}",
"rotation": "{0, 0, 0}",
"condition": "(((configID=='v2')||(configLadderID=='v2'))&&(cubeHeight==400))",
"maxConnections": 1,
"stepX": 400,
"stepY": 0,
"stepZ": 0,
"assignmentScripts": {
"onDock": "index = connection.index;
if(index=='0'){bridgePos1InfillType='cubby'};
if(index=='1'){bridgePos2InfillType='cubby'};
if(index=='2'){bridgePos3InfillType='cubby'}",
"onUpdate" : "",
"onUnDock": "index = connection.index;
if(index=='0'){bridgePos1InfillType='blank'};
if(index=='1'){bridgePos2InfillType='blank'};
if(index=='2'){bridgePos3InfillType='blank'}"
}
But what I want to happen is when I then try to docking a different part (using Docking Range) it should not let me as the InFillType is not BLANK
this is my next part to dock but how do I check to see if the Docking Range Position in already taken or that the bridgePos1InFillType is not BLANK...
{
"mask": "cupboard400x400",
"position": "{0, 0, 40}",
"stepEnd": "{800, 0, 0}",
"rotation": "{0, 0, 0}",
"condition": "index = connection.index;
(((configID=='v2')||(configLadderID=='v2'))&&(cubeHeight==400))",
"maxConnections": 1,
"stepX": 400,
"stepY": 0,
"stepZ": 0
},

You can utilize internal value called condition in all condition statements, like this:
"condition": "
/* you probably want to check if you're in preview, otherwise you could delete it right after docking, if the condition result becomes false */
if (connection.isPreview) {
/* then checking against which internal var to check this */
if (connection.index == 1) { condition = bridgePos1InfillType == 'blank'; }
...
/* then you can do further checks like */
condition = condition && (!maximumLoadExceeded);
} else {
/* if it is already there, just don't worry & keep it */
condition = true;
}
"
There is connection.index or connection.position to check the current position in the range's array. For 2D ranges, I recommend approach of computing the indices from connection.position. For 1D range, connection.index works just fine. See https://docs.roomle.com/scripting/resources/200_80_dockingbasics.html#docking-ranges and https://docs.roomle.com/scripting/resources/200_140_advanceddockinglogic.html#docking-ranges
I also see that an array would be good for you. If you worry only about whether there is something already docked, you could do an array of booleans occupied = [0,0,0] and then access the values using get(self.occupied, connection.index) in condition and use the set function in assignment scripts. If you need to also know what is occupied, RoomleScript unfortunately only supports arrays of numbers. A possible solution would be to have a 1:1 constant numeric identifier for every type.
See also https://docs.roomle.com/scripting/resources/200_40_roomlescript.html#arrays

Related

How to draw a number axis with arrow

I need to draw a horizontal axis to decribe number, currently there is a NumberLine which can work, however there is no arrow to indicate the positive direction.
Is there built-in object to do that? or manually create such class with code?
If you have doubts about how an object works, you can look for it in the source code manimlib/mobject/
In your case it is in number_line.py, the code is not difficult to understand and thus you avoid asking.
class NumberLineExample(Scene):
def construct(self):
nl=NumberLine(x_min=-2,
x_max=2,
include_numbers=True,
include_tip=True,
label_direction=UP
)
self.add(nl)
If you see the source code you find all options:
class NumberLine(Line):
CONFIG = {
"color": LIGHT_GREY,
"x_min": -FRAME_X_RADIUS,
"x_max": FRAME_X_RADIUS,
"unit_size": 1,
"include_ticks": True,
"tick_size": 0.1,
"tick_frequency": 1,
# Defaults to value near x_min s.t. 0 is a tick
# TODO, rename this
"leftmost_tick": None,
# Change name
"numbers_with_elongated_ticks": [0],
"include_numbers": False,
"numbers_to_show": None,
"longer_tick_multiple": 2,
"number_at_center": 0,
"number_scale_val": 0.75,
"label_direction": DOWN,
"line_to_number_buff": MED_SMALL_BUFF,
"include_tip": False,
"tip_width": 0.25,
"tip_height": 0.25,
"decimal_number_config": {
"num_decimal_places": 0,
},
"exclude_zero_from_default_numbers": False,
}

Call on a String as an Array

I have been stuck on a problem for a couple days now and I'm not sure how to word it, so I haven't had much luck searching for an answer hoping you can help!
I have an array of 20 arrays which is the same as my array of 20 strings, when a button is clicked the array changes to the name of the selected button, I can change it to a string very easily with:
newArray = oldArray[0]
But cannot find a way to insert the string name as the new array name, if anyone has any advice I'd be glad, thanks!
newArray = [oldArray[0]]
Just creates an array with one string in, I have been looking for a function something like:
newArray = Array(named: oldArray [0])
but not sure if it exists.
If you want to insert on some specific index then use insert like this:
var newArr = [String]()
newArr.insert("jogendar", at: 0) /// Removes and returns the element at the specified position.
And if you want to add at the end then use append like this:
var newArr = [String]()
newArr.append("jogendar") ///Adds the elements of a sequence to the end of the array.
If you want to change the name of the button when selected then you don't need to maintain the array, you can set the title of button for states like this:
let btn = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 30))
btn.setTitle("jogendar_defautl", for: UIControlState.normal)
btn.setTitle("jogendar_selected", for: UIControlState.selected)
And when you want to select the button then button name auto change according to selected state:
btn.isSelected = !btn.isSelected
This is not an answer as such, you do not provide enough detail for that, but let's see if we can help you onto the right track. You wrote:
I have been looking for a function something like:
newArray = Array(named: oldArray [0])
This does not exist because arrays (and other values) do not have names. You appear to be confusing variable names and values, they are very different things. For example, the expression:
[0, 1, 8, 27, 64]
produces an array value containing five elements, it has no name it is just a value. The value could be assigned to a variable:
var cubes = [0, 1, 8, 27, 64]
Now we have a variable with the name cubes whose current value is the array. The above statement has both declared a variable, cubes, given it a type, [Int], and assigned it an initial value, [0, 1, 8, 27, 64]. It has not named the value, the value can be copied into another variable:
var anotherVariable = cubes
After the above you have two variables, cubes and anotherVariable, each which currently has the same value. Values are not shared, you can alter one of the values:
anotherVariable[0] = 42
and now anotherVariable contains the value [42, 1, 8, 27, 64] while cubes still contains the value [0, 1, 8, 27, 64].
OK, back to your question, you wrote:
I have an array of 20 arrays which is the same as my array of 20 strings...
What you appear to be describing is something along the lines of:
var myArrays = [ [0, 1, 4, 9, 16], [0, 1, 8, 27, 64] ]
var myNames = [ "squares", "cubes" ]
and you have buttons with labels squares and cubes. What you appear to be attempting is to go from a click on a button to an element of myArrays via the myNames array - which is where you "naming" of values is coming from.
To map a "name" to a "array" you should use a dictionary, which is a collection keys, your names, to values, your arrays:
let myCollection = [ "squares":[0, 1, 4, 9, 16], "cubes":[0, 1, 8, 27, 64] ]
This declares a constant (the use of let rather than var) dictionary whose keys are strings and values arrays. So you effectively have a collection of "named arrays".
You can now select an array for this collection by name using a statement along the lines of:
var currentArray = myCollection[currentButtonTitle]
where currentButtonTitle has previously been set to the title of the button you clicked.
HTH

How to create textbox in love2d

I have been trying to make a box for a user to input data once the user clicks the box. This is what I have so far, but clicking the box does not cause text input to be added to it. Where is the issue?
function love.load ()
txt1 = ""
columnx = { 50, 160, 260, 375, 495, 600, 710 }
columny = { 130, 230, 330, 440, 540, 640 }
if show1 == true then
function love.textinput(t)
txt1 = t
end
end
end
function love.mousepressed (x, y)
if
x >= columnx[4] and
x <= 435 and
y >= columny[1] and
y >= 145 then
show1 = true
end
end
function love.draw ()
love.graphics.print(txt1, columnx[4], columny[1])
end
You're pretty much on the way, but I'll give you some tips on how to create a basic textbox.
Firstly, consider a conceptual textbox to be a singular table, that contains everything it needs to know about itself to properly update and render. It's much easier to reason about something that is self contained.
A textbox needs to know its position, its size, whether it is active, and which text it contains. We can condense that into a table that looks like the following.
local textbox = {
x = 40,
y = 40,
width = 400,
height = 200,
text = '',
active = false,
colors = {
background = { 255, 255, 255, 255 },
text = { 40, 40, 40, 255 }
}
}
(We also store some color info.)
After that the simple way to add text is through love.textinput, as you've seen. In your code we only check if the textbox is active once, in love.load, which it of course isn't since we likely haven't taken any user input yet. Instead of attempting to overload the function, we simply check if the textbox is active or not inside the handler, and proceed accordingly.
function love.textinput (text)
if textbox.active then
textbox.text = textbox.text .. text
end
end
We've covered how to check if the user has clicked within a rectangular area in the question: Love2d cursor positions. We want to deactivate the textbox if it is currently active and the user clicks outside of its space.
function love.mousepressed (x, y)
if
x >= textbox.x and
x <= textbox.x + textbox.width and
y >= textbox.y and
y <= textbox.y + textbox.height
then
textbox.active = true
elseif textbox.active then
textbox.active = false
end
end
And finally we need to render our textbox. We use unpack to expand our color tables, and love.graphics.printf to make sure our text wraps within our textbox's space.
function love.draw ()
love.graphics.setColor(unpack(textbox.colors.background))
love.graphics.rectangle('fill',
textbox.x, textbox.y,
textbox.width, textbox.height)
love.graphics.setColor(unpack(textbox.colors.text))
love.graphics.printf(textbox.text,
textbox.x, textbox.y,
textbox.width, 'left')
end
These are the basic ideas of creating a very rough textbox. It's not perfect. Note that you will need to consider what happens when the text gets longer height-wise than we initially set our textbox's height to, as the two are only loosely related.
To make your program easier to read, and easier to extend, everything you've seen above should really be placed into their own functions that handle textbox tables, instead of cluttering the love handlers with common code. Take a look at Chapter 16 of Programming in Lua, which covers Object-Oriented Programming - a generally vital topic for game development.
See the love.textinput page on how to handle a backspace, to delete characters.
Some extra things to think about:
How can we distinguish an active textbox from an inactive one?
How can we create a list of textboxes, so we can have multiple on screen (but only one active)?
I guess, the love.textinput() callback function is what you are looking for. But as the term 'callback' implies, it will be called by the LÖVE engine, not your code. It will be called whenever the user inputs some text while your game is running. Therefore you need to put it outside the love.load() function.
In the love2d.org wiki is an example for this (the lower one).
As for your example, move the love.textinput() out of love.load() and add an if statement:
function love.load()
txt1 = ""
columnx = {50, 160, 260, 375, 495, 600, 710}
columny = {130, 230, 330, 440, 540, 640}
end
function love.textinput(t)
if (show1) then
txt1 = txt1 .. t
end
end
-- The rest of your code.
-- And maybe mix it with the 'backspace example' from the wiki...
-- But you also might want to have some function to set 'show1' back to 'false' after the text input. Maybe something like this:
function love.keypressed(key)
if (key == "return") and (show1) then
show1 = false
end
end
I hope I could help you a bit!

Creating layers named after slices

I have an issue that may well not be possible to solve 'out of the box'. I'm running fireworks 8 and would like to to be able to run the:
Commands->Document->Split to layers
and have the resulting layers created using the name of the slice that it created the layer for. So for example, if I have 3 slices in my png, called 'Head', Shoulder' and 'Arm', I'd like that command to create the layer name that corresponds to the slice name. As it currently stands, when running this command, the default layer names created are sequentially named 'Layer 1', 'Layer 2', 'Layer 3' etc etc.
The reason for this requirement is due to the fact that I then wish to use the Export command to save the individual Layers out to named png files that use the 'Slice names ('Head.png' etc), rather than the default Layer names. Now I know I can manually rename the layers to match the Slices, and Export to a folder as required. However, in my real life scenario, I have over 50 Slices per document that require this treatment and I have 100's of documents at a time to 'batch' process. So my idea was that I would be able to run a command (or create some sort of macro) that would allow me to create the layers with the same name as the slice that they contained.
This would make my life SOO much simpler as I could then totally automate the process based on a set of source images located within a folder structure, rather than opening each file, running the above command, renaming each layer manually (error prone of course) and then running the Export function.
Can anyone offer advice on finding a solution to this?? I hope I'm not the only one to have come across this requirement.
I've added a line to the Split to Layers command here. If you save that as Distribute to Named Layers.jsf in the same folder as the original command (in CS5.5 I found it in Configuration/Commands/Document), I think that should do what you need.
That said, jsf is fairly unpredictable in my experience (for example, that command seems to miss the default name of things like Rectangles until they've been renamed), so I'm not certain if it will work 100% of the time. Also, the script skips over the Web Layer which contains slices in 5.5 - I can't remember if the set up is different in 8. Hopefully that gets you some of the way there though.
FYI - here's what the final programatic fix amounted to (with thanks to david mear)
// This command will take multiple objects and move them to indivdual layers
// and then prompt for a folder location to save the layers as named-layer.png
// files. This is ultra useful if you want to save slices out to individual
// files and wish to have total control over the resulting file names
var curDoc = fw.getDocumentDOM();
// Save the current frame in the document
var curFrameNum = curDoc.currentFrameNum;
// get the total layers minus the web layer
var numLayers = curDoc.layers.length - 1; // skip the web layer.
var curLayerNum;
// default to d:\ for now
var locFolder = fw.browseForFolderURL("select a folder", "file:///d|/");
// 23/3/2013 add dialog box for file
if (locFolder !== null) {
// loop through the current number of layers
for (curLayerNum = numLayers - 1; curLayerNum >= 0; curLayerNum--) {
// get the current layer
var curLayer = curDoc.layers[curLayerNum];
// get the elements on the current layer
var elements = curLayer.frames[curFrameNum].elements;
//if layer is locked cannot distribute so continue to next layer.
if (curLayer.frames[curFrameNum].locked == true)
continue;
// get the number of elements
var numElements = elements.length - 1;
var i;
// loop through the number of elements
for (i = 0; i < numElements; i++) {
// get the current layer number
if (i == 0) curDoc.currentLayerNum = curLayerNum;
// add layers for the number of elements
curDoc.addNewLayer(null, false);
}
// again loop through the number of elements
for (i = 0; i < numElements; i++) {
// set the current layer
curLayer = curDoc.layers[curLayerNum];
// get the elements on the current layer
elements = curLayer.frames[curFrameNum].elements;
// select none
curDoc.selectNone();
// create a new array that will hold the selection
var sel = new Array();
// populate the array
sel[0] = elements[elements.length - 2];
// EDIT - 25/3/2013 rename target layer if element has a name
curDoc.setLayerName(curLayerNum + i + 1, sel[0].name || "");
// select all of the elements of the array in Fireworks
fw.selection = sel;
// move the selection to its new layer
curDoc.moveSelectionToLayer(curLayerNum + i + 1, false, "none", -1);
}
}
// EDIT - 25/3/2013 set to png32 export option
set_export_as_png_32(curDoc);
fw.exportLayers(curDoc, locFolder);
}
function set_export_as_png_32(targetDoc) {
targetDoc.setExportOptions(
{
animAutoCrop: true,
animAutoDifference: true,
applyScale: false,
colorMode: "32 bit",
crop: false,
cropBottom: 0,
cropLeft: 0,
cropRight: 0,
cropTop: 0,
ditherMode: "none",
ditherPercent: 100,
exportFormat: "PNG",
frameInfo: [],
interlacedGIF: false,
jpegQuality: 80,
jpegSelPreserveButtons: false,
jpegSelPreserveText: true,
jpegSelQuality: 90,
jpegSelQualityEnabled: false,
jpegSmoothness: 0,
jpegSubsampling: 0,
localAdaptive: true,
lossyGifAmount: 0,
macCreator: "",
macFileType: "",
name: "PNG32",
numCustomEntries: 0,
numEntriesRequested: 0,
numGridEntries: 6,
optimized: true,
paletteEntries: null,
paletteInfo: null,
paletteMode: "adaptive",
paletteTransparency: "none",
percentScale: 100,
progressiveJPEG: false,
savedAnimationRepeat: 0,
sorting: "none",
useScale: true,
webSnapAdaptive: false,
webSnapTolerance: 14,
xSize: 0,
ySize: 0
}
);
}

Apply merge layers on multiple images without losing layers

My problem:
I want to make a lot of images that differ only in three text layers. I have already figured out, how to make the text changes with the Python-Fu Console. My next step is to put this text change code into a loop and add a png.file-save-png(...) to save the picture. In order to save as a PNG I have to merge all my layers (for each image), which is no problem with single_layer = pdb.gimp-image-merge-visible-layers(image,0). In order to keep working from here, I would need to to an undo, to get my old layers back.
Do I need to apply an UNDO Operation in GIMP from a script?
I couldn't find any hint on this feature. Maybe anyone knows how to do this, or has a workaround.
After a night of sleep, I figured out a workaround:
I reopened the base image file for each card in the loop, where all layers and text layers stayed intact. That prevented me from needing the undo.
By the way, here is my script for creating 4 * 13 playing cards (from ones own base_card.xcf):
basefile = "/home/[...]/base_card.xcf"
basesave = "/home/[...]/"
color_blue = [ (32.0 /255.0, 74.0/255.0,135.0/255.0,1.0),
(52.0 /255.0,101.0/255.0,164.0/255.0,1.0)]
color_red = [ (164.0/255.0, 0.0/255.0, 0.0/255.0,1.0),
(204.0/255.0, 0.0/255.0, 0.0/255.0,1.0)]
color_yellow = [ (196.0/255.0,160.0/255.0, 0.0/255.0,1.0),
(237.0/255.0,212.0/255.0, 0.0/255.0,1.0)]
color_green = [ ( 78.0/255.0,154.0/255.0, 6.0/255.0,1.0),
(115.0/255.0,210.0/255.0, 22.0/255.0,1.0)]
def createCard(color_list, color_name, number):
pdb.gimp_context_set_foreground(color_list[1])
image = pdb.gimp_file_load(basefile, basefile)
textlayers = image.layers[0:3]
for layer in textlayers:
pdb.gimp_text_layer_set_text(layer, number)
pdb.gimp_text_layer_set_color(layer, color_list[0])
layer = image.layers[3]
pdb.gimp_edit_bucket_fill(layer, 0, 0, 100, 0, 0, 30, 30)
layer = pdb.gimp_image_merge_visible_layers(image, 0)
savename = "%s%s_%s.png" % (basesave, color_name, number)
pdb.file_png_save(image, layer, savename, savename, 0, 0, 0, 0, 0, 0, 0)
image = None
for c in range(1,14):
createCard(color_blue, "BLUE", c)
for c in range(1,14):
createCard(color_yellow, "YELLOW", c)
for c in range(1,14):
createCard(color_red, "RED", c)
for c in range(1,14):
createCard(color_green, "GREEN", c)

Resources