Packing rectangles in a rectangle, generate grids coordinates - php

Looking to generate some randoms grids of rectangles in a bigger rectangle.
This looks like a fairly easy problem, but it's not, seeking for advises here.
This is not a packing problem, since the inner rectangles can have any width and height.
But the amount of rectangles aren't always the same.
I have some results already with different kinds of loops, but none are really efficient.
For example, with 15 rectangles, a possible way to represent them could be:
O 10 50 60
+----------+---------------------------------------------+----------+-------+
| | | | |
| | | | |
| | | | |
5 +----------+---------------------------------------------+----------+-------+
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
15+----------+---------------------------------------------+----------+-------+
| | | | |
| | | | |
| | | | |
+----------+---------------------------------------------+----------+-------+
| | | | |
| | | | |
| | ↓ | | |
+----------+---------------------------------------------+----------+-------+
The coordinates would then be something like an array of [x,y] point of the top left corner (+) of each inner squares:
[[0,0],[10,0],[50,0],[60,0],[5,0],[5,10],[5,50], ...]
Or even better an array of [x,y,w,h] values (Top left x, top left y, width, height)
[[0,0,10,5],[10,0,40,5],[50,0,10,5],[60,0,10,5],[5,0,10,10],[5,10,40,10],[5,50,20,10], ...]
But the goal is to make a function that generate coordinates for any amount of inner squares:
For example, with 14 rectangles, a possible way to represent them could be:
+----------+----------------------------------+---------------------+-------+
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
+----------+----------------------------------+----------+----------+-------+
| | | | |
| | | | |
+----------+--------+------------------------------------+----------+-------+
| | | | |
| | | | |
+-------------------+-----------+------------------------+----------+-------+
| | |
| | |
| | |
| | |
+-------------------------------+-----------------------------------+-------+
Related links:
Packing rectangular image data into a square texture
What algorithm can be used for packing rectangles of different sizes into the smallest rectangle possible in a fairly optimal way?
How to arrange N rectangles to cover minimum area

Your problem has two aspects: You want to create a regular grid with n rectangles that may span several cells and you want to distribute the coordinates of the cell borders randomly.
So I propose the foolowing algorithm:
Determine the number of rows and columns in your grid so that each cell is more or less square.
Create a rectangular grid of 1×1 cells. The number of cells will be greater than n.
Conflate two adjacent cells until you have n cells.
Now create random axes for the cell boundaries. If you have m columns, create an array of ncreasing values so that the first coordinate is 0 and the last coordinate is the width of the original ractangle. You can do this by creating a list of increasing random numbers and then normalizing with the overall width.
Finally, create the actual rectangles, using the information of the cells (position, cell span) and the random axes.
This algorithm uses two representations of the rectangles: First, it creates "cells", which have information about their row and column indices and spans. The actual output are rectangles with left, top, width and height information.
I'm not familar with PHP, so here is an implementaion in Javascript. I think you can see how it works:
function tile(big, n) {
// big: outer rectangle
// n: number of subrectangles to create
// determine number of rows and cols
let l = Math.sqrt(big.height * big.width / n);
let ncol = (big.width / l + 1) | 0;
let nrow = (big.height / l + 1) | 0;
let cells = [];
// create grid of m*m cells
for (let j = 0; j < nrow; j++) {
for (let i = 0; i < ncol; i++) {
cells.push(new Cell(i, j, 1, 1));
}
}
// conflate rectangles until target number is reached
while (cells.length > n) {
let k = (cells.length * Math.random()) | 0;
let c = cells[k];
if (c.col + c.colspan < ncol) {
let cc = cells[k + 1];
c.colspan += cc.colspan;
cells.splice(k + 1, 1);
}
}
// generate increasing lists of random numbers
let xx = [0];
let yy = [0];
for (let i = 0; i < ncol; i++) {
xx.push(xx[xx.length - 1] + 0.5 + Math.random());
}
for (let i = 0; i < nrow; i++) {
yy.push(yy[yy.length - 1] + 0.5 + Math.random());
}
// fit numbers to outer rectangle
for (let i = 0; i < ncol; i++) {
xx[i + 1] = (big.width * xx[i + 1] / xx[ncol]) | 0;
}
for (let i = 0; i < nrow; i++) {
yy[i + 1] = (big.height * yy[i + 1] / yy[nrow]) | 0;
}
// create actual rectangles
let res = [];
for (let cell of cells) {
let x = xx[cell.col];
let w = xx[cell.col + cell.colspan] - x;
let y = yy[cell.row];
let h = yy[cell.row + cell.rowspan] - y;
res.push(new Rect(x, y, w, h));
}
return res;
}
Notes:
The code above conflates cells only horizontally. You can change this to conflate cells vertically or both, but as the algorithm is at the moment, you won't be able to create cells that are more than one cell wide and high without major modifications.
The x | 0 is a cheap way to turn floating-point numbers into integers. I've used it to snap the final coordinates to integer values, but you can also snap them to any grid size s with s * ((x / s) | 0) or s * intval(x / s) in PHP.
The code doesn't care much about aesthetics. It picks the cell sizes and the cells to conflate randomly, so that you might get cross joints, which don't look nice. You can influence the regularity of the result a bit, though:
When you determine the number of columns and rows, you must add one to the result, so that you get cells to conflate in every case. (Even if you have a square and pass a square number as n, you will get joined rectangles.) If you add more, you will get more joined rectangles and the result will look more irregular.
Math.random() returns a random number between 0 and 1. When creating the axes, I've added 0.5 to the result, so that you don't get very narrow cells. If you add less, the coordinates will be more irregular, if you add more, they will be more evenly distributed.
Perhaps you can get a good effect from making the row coordinates even and the clumns coordinates irregular.
You've mentioned good-looking properties in a comment. Perhaps it is easier to create a good-looking structure when you create the grid and placing the rectangles with constraints instead of first creating a grid and then remoing joints.

Related

Join arrays into one associative array

I am saving many waypointIDs, bearings, and distances in three columns in a single row of my database. Each column stores it's respective item separated by a comma.
WaypointIds: 510,511,512
Bearings: 65,50,32
Distances: 74,19,14
I think I might have coded myself into a corner! I now need to pull them from the database and run through adding them to a table to output on screen.
I'm using the following to put the corresponding columns back into an array but am a little stuck with where to go after that, or if that is even the way to go about it.
$waypointArrays = explode (",", $waypointsString);
$bearingArrays = explode (",", $bearingsString);
$waypointsString & $bearingsStrings are the variables set by my database call.
I think I need to add them all together so I can iterate through each one in turn.
For example, waypointId 510, will have bearing 065, and distance 0.74.
I think I need an associative array but not sure how to add them all together so I can run through each waypoint ID and pull out the rest of the entry.
e.g. for each waypointId give the corresponding bearing and waypoint.
I have checks in place to ensure that as we add waypoints/bearings/distances that they don't fall out of step with each other so there will always be the same number of entries in each column.
Don't continue with this design: your database is not normalised and therefore you are not getting any benefit from the power that a database can offer.
I don't think working around this problem by extracting the information in PHP using explode, ..etc is the right way, so I will clarify how your database should be normalised:
Currently you have a table like this (possibly with many more columns):
Main table: route
+----+---------+-------------+----------+-----------+
| id | Name | WaypointIds | Bearings | Distances |
+----+---------+-------------+----------+-----------+
| 1 | myroute | 510,511,512 | 65,50,32 | 74,19,14 |
| .. | .... | .... | .... | .... |
+----+---------+-------------+----------+-----------+
The comma-separated lists violate the first normal norm:
A relation is in first normal form if and only if the domain of each attribute contains only atomic (indivisible) values, and the value of each attribute contains only a single value from that domain.
You should resolve this by creating a separate table for each of these three columns, which will have one record for each atomic value
Main table: route
+----+---------+
| id | Name |
+----+---------+
| 1 | myroute |
| .. | .... |
+----+---------+
new table route_waypoint
+----------+-------------+------------+----------+
| route_id | waypoint_id | bearing_id | distance |
+----------+-------------+------------+----------+
| 1 | 510 | 65 | 74 |
| 1 | 511 | 50 | 19 |
| 1 | 512 | 32 | 14 |
| 2 | ... | .. | .. |
| .. | ... | .. | .. |
+----------+-------------+------------+----------+
The first column is a foreign key referencing the id of the main table.
To select the data you need, you could have an SQL like this:
select route.*, rw.waypoint_id, rw.bearing_id, rw.distance
from route
inner join route_waypoints rw on rw.route_id = route.id
order by route.id, rw.waypoint_id
Now PHP will receive the triplets (waypoint, bearing, distance) that belong together in the same record. You might need a nested loop while the route.id remains the same, but this is how it is done.
To answer your question, code below will work as long as waypointsIds are unique. That beign said, as other mentioned, fix your database layout. What you have here could really benefit from a separate table.
<?php
$waypointIds = [510, 511, 512];
$bearings = [65, 50, 32];
$distances = [74, 19, 14];
$output = [];
for ($i = 0; $i < count($waypointIds); $i++) {
$output[$waypointIds[$i]] = [
'bearing' => $bearings[$i],
'distance' => $distances[$i]
];
}
print_r($output);
$waypoints = array(510, 511, 512);
$bearings = array(65, 50, 32);
$distances = array(74, 19, 14);
for ($i = 0; $i < count($waypoints); $i++) {
$res[] = array(
'waypoint' => $waypoints[$i],
'bearing' => sprintf("%03d", $bearings[$i]),
'distance' => $distances[$i]/100
);
}
print_r($res);

Replace table name and print all colors per column

Here's my query:
mysql>
select DATE_FORMAT(STR_TO_DATE(items.date, '%Y-%m-%d'), '%M %d, %Y') date_new, unit, Descpt, p_cost, add_by, GROUP_CONCAT(color,' = ',qty) as COLOR, SUM(qty) as Total, SUM(qty*(p_cost)) as Ptotal from items where status IN ('1','2') Group By Descpt;
+---------------+------+------------------------+--------+--------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------+-----------+
| date_new | unit | Descpt | p_cost | add_by | COLOR | Total | Ptotal |
+---------------+------+------------------------+--------+--------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------+-----------+
| June 16, 2016 | Pcs | #1688N - Big Crate New | 125.00 | Psd | BLUE - W = 200,BLUE - W/O = 400,RED - W/O = 300,RED - W = 500,GREEN - W = 300,GREEN - W/O = 200,YELLOW - W = 400,YELLOW - W/O = 582,BLACK - W = 255,BLACK - W/O = 330 | 3467 | 900927.75 |
Color values will be print as column. how can I achieve it by select statement?
attached is the needed output.
This is not the complete answer, but I believe it will show you the way...
The ideia is to get the COLOR column into the PHP realm and them make some regular expressions to get the data... after that, draw the table as you wish...
$color = "BLUE - W = 200,BLUE - W/O = 400,RED - W/O = 300,RED - W = 500,GREEN - W = 300,GREEN - W/O = 200,YELLOW - W = 400,YELLOW - W/O = 582,BLACK - W = 255,BLACK - W/O = 330";
if(preg_match_all("/(\w+)\s*\-\s*(\S+)\s*=\s*(\d+)/",$color,$m)) {
print_r($m);
}
else {
print "Regexp failed!\n";
}
array $m will have all the data you need separated by lines and columns.
Note that the regular expression may need some adjustements if the data differs for other results, for example if some numbers are missing somewhere...

mysql query how to fill in blank rows using the value of its top row with content?

I need a mysql query to do this
But I can't think of something to make this happened
We have an excel file and that contains more than 5,000 rows
some are blank and others have value what I manually do is
to copy top value to its below blank fields and so on
so I think a way that I can import the excel into myslq database
and make some query to save effort and time in terms of doing this..
and this is my table looks like:
only column Date-paid has value for the entire rows but some has value and other don't have see this example but the original file is more than 5,000 rows
|Name | age | Salary| Date_paid |
-----------------------------------
bbjake| 16 | 200 | 03/25/2015 |
----------------------------------|
| | | 04/25/2015 |
----------------------------------
| | | 05/25/2015 |
-----------------------------------
kentot| 20 | 300 | 03/25/2015 |
-----------------------------------
| | | 04/25/2015 |
----------------------------------|
| | | 05/25/2015 |
-----------------------------------
and after running the query this is my desired result
|Name | age | Salary| Date_paid |
----------------------------------|
bbjake| 16 | 200 | 03/25/2015 |
-----------------------------------
bbjake| 16 | 200 | 04/25/2015 |
-----------------------------------
bbjake| 16 | 200 | 05/25/2015 |
-----------------------------------
kentot| 20 | 300 | 03/25/2015 |
-----------------------------------
kentot| 20 | 300 | 04/25/2015 |
-----------------------------------
kentot| 20 | 300 | 05/25/2015 |
-----------------------------------
So blank fields will be filled in with the value on the raw with content and
drag down until with another raw with value in short will fill in blank rows with
the content of top row
table is manually imported into the database and let just say columns are only three Name(varchar) Age(int) Salary (int) Date Paid (Date) Table name = "Emplyoee" Database name = "Company"
your question is strange...why would somebody wanna do this?
Anyway, to do this, it's interesting, how this table is created / generated. Your question is very weird...i think you want to know, how to fill this table AFTER the server built it and created the HTML table (because of your post title here). On the other side, this "php" tag does not make any sense in this scope....
JavaScript solution:
var trs = document.getElementsByTagName('tr');
var innerHTML = "";
for (var i = 0; i < trs.length; i++){
if (trs[i].className !== "heading") {
innerHTML = (trs[i].innerHTML.children[0] == "") ? innerHTML : trs[i].innerHTML;
trs[i].innerHTML = innerHTML;
}
}
note that you have to set the classname of the description rows to "heading" in this example, that this will not be interpreted from the script. If your server alone shall do this, just follow the same logic like in my JavaScript. Always check, if next element would be empty - if yes: fill in the same string like before; if not: take the new string and fill it into all next empty cells, etc....

How to tabulate this javascript array in a mysql database? What index should I use?

My question is about tabulating data in MySql. I was wondering, how to best represent this javascript array in MySql? What index should I use? I'm going to use the data to populate a javascript array via PHP.
A[i] represents a card. B[i] represents a matching card.
A = new Array();
A[0] = new Array();
A[0][0]='eat';
A[0][1] = 1;
A[0][2] = 0;
A[1] = new Array();
A[1][0]='drink';
A[1][1] = 2;
A[1][2] = 0;
B = new Array();
B[0] = new Array();
B[0][0]='tacos';
B[0][1] = 1;
B[0][2] = 0;
B[1] = new Array();
B[1][0]='tequila';
B[1][1] = 2;
B[1][2] = 0;
I need to be able to uniquely identify components within the array later, so that I can use parts of the data to populate new arrays (So I can use and combine different cards into a new array). For example, I might want to populate a new array in javascript using A[0][0], A[0][1], A[0][2],B[0][0], B[0][1] and info from another array stored in the MySql (Lets say Y[2][0], Y[2][1],Y[2][2],Z[2][0], Z[2][1]).
This is what I've come up with so far.
-----------------------------------------
| card pair | card |card info|Tag|Tag2|
-----------------------------------------
| 1 | A | eat | 1 | 0 |
| 1 | B | tacos | 1 | - |
| 2 | A | drink | 2 | 0 |
| 2 | B | tequila | 2 | - |
-----------------------------------------
Maybe I need to add a primary index to the above one?
-------------------------------
|card pair |card info|Tag|Tag2|
-------------------------------
| 1A | eat | 1 | 0 |
| 1B | tacos | 1 | - |
| 2A | drink | 2 | 0 |
| 2B | tequila | 2 | - |
-------------------------------
I thought the card pair could be the index. Not sure if this is possible or a good idea. Also not sure what type of index I would use if I did.
If you have a better way to tabulate the data or can recommend what type of index to use I'd much appreciate it.
EDIT: I think I can do away with the last 2 columns (Tag and Tag2), so I think I might just use the table as below.
----------------------
|card pair |card info|
----------------------
| 1A | eat |
| 1B | tacos |
| 2A | drink |
| 2B | tequila |
----------------------
Should I add an incrementing index to the table? Is the card pair sufficient as the index?If yes, what is the best index type to use?
Thanks!
well, from a database perspective, you will want to 'normalize' this information.
I think it would be more like this:
card
------------
card_id
info
card_pair
------------
card_1_id
card_2_id

How could I detect if a character close to another character on a QWERTY keyboard?

I'm developing a spam detection system and have been alerted to find that it can't detect strings like this - "asdfsdf".
My solution to this involves detecting if the previous keys were near the other keys on the keyboard. I am not getting the input (to detect spam from) from the keyboard, I'm getting it in the form of a string.
All I want to know is whether a character is one key, two keys or more than two keys away from another character.
For example, on a modern QWERTY keyboard, the characters 'q' and 'w' would be 1 key away. Same would the chars 'q' and 's'. Humans can figure this out logically, how could I do this in code?
You could simply create a two-dimensional map for the standard qwerty keyboard.
Basically it could look something like this:
map[0][0] = 'q';
map[0][1] = 'a';
map[1][0] = 'w';
map[1][1] = 's';
and so on.
When you get two characters, you simply need to find their x, and y in the array 'map' above, and can simply calculate the distance using pythagoras. It would not fill the requirement you had as 'q' and 's' being 1 distance away. But rather it would be sqrt(1^2 + 1^2) approx 1.4
The formula would be:
Characters are c1 and c2
Find coordinates for c1 and c2: (x1,y1) and (x2,y2)
Calculate the distance using pythagoras: dist = sqrt((x2-x1)^2 + (y2-y1)^2).
If necessary, ceil or floor the result.
For example:
Say you get the characters c1='q', and c2='w'. Examine the map and find that 'q' has coordinates (x1,y1) = (0, 0) and 'w' has coordinates (x2,y2) = (1, 0). The distance is
sqrt((1-0)^2 + (0-0)^2) = sqrt(1) = 1
Well, let's see. That's a tough one. I always take the brute-force method and I stay away from advanced concepts like that guy Pythagoras tried to foist on us, so how about a two-dimensional table? Something like this. maybe:
+---+---+---+---+---+---+---
| | a | b | c | d | f | s ...
+---+---+---+---+---+---+---
| a | 0 | 5 | 4 | 2 | 4 | 1 ...
| b | 5 | 0 | 3 | 3 | 2 | 4 ...
| c | 4 | 3 | 0 | 1 | 2 | 2 ...
| d | 2 | 3 | 1 | 0 | 1 | 1 ...
| f | 3 | 2 | 2 | 1 | 0 | 2 ...
| s | 1 | 4 | 2 | 1 | 2 | 0 ...
+---+---+---+---+---+---+---
Could that work for ya'? You could even have negative numbers to show that one key is to the left of the other. PLUS you could put a 2-integer struct in each cell where the second int is positive or negative to show that the second letter is up or down from the first. Get my patent attorney on the phone, quick!
Build a map from keys to positions on an idealized keyboard. Something like:
'q' => {0,0},
'w' => {0,1},
'a' => {1,0},
's' => {1,1}, ...
Then you can take the "distance" as the mathematical distance between the two points.
The basic idea is to create a map of characters and their positions on the keyboard. You can then use a simple distance formula to determine how close they are together.
For example, consider the left side of the keyboard:
1 2 3 4 5 6
q w e r t
a s d f g
z x c v b
Character a has the position [2, 0] and character b has the position [3, 4]. The formula for their distance apart is:
sqrt((x2-x1)^2 + (y2-y1)^2);
So the distance between a and b is sqrt((4 - 0)^2 + (3 - 2)^2)
It'll take you a little bit of effort to map the keys into a rectangular grid (my example isn't perfect, but it gives you the idea). But after that you can build a map (or dictionary), and lookup is simple and fast.
I developed a function for the same purpose in PHP because I wanted to see whether I can use it to analyse strings to figure out whether they're likely to be spam.
This is for the QWERTZ keyboard, but it can easily be changed. The first number in the array $keys is the approximate distance from the left and the second is the row number from top.
function string_distance($string){
if(mb_strlen($string)<2){
return NULL;
}
$keys=array(
'q'=>array(1,1),
'w'=>array(2,1),
'e'=>array(3,1),
'r'=>array(4,1),
't'=>array(5,1),
'z'=>array(6,1),
'u'=>array(7,1),
'i'=>array(8,1),
'o'=>array(9,1),
'p'=>array(10,1),
'a'=>array(1.25,2),
's'=>array(2.25,2),
'd'=>array(3.25,2),
'f'=>array(4.25,2),
'g'=>array(5.25,2),
'h'=>array(6.25,2),
'j'=>array(7.25,2),
'k'=>array(8.25,2),
'l'=>array(9.25,2),
'y'=>array(1.85,3),
'x'=>array(2.85,3),
'c'=>array(3.85,3),
'v'=>array(4.85,3),
'b'=>array(5.85,3),
'n'=>array(6.85,3),
'm'=>array(7.85,3)
);
$string=preg_replace("/[^a-z]+/",'',mb_strtolower($string));
for($i=0;$i+1<mb_strlen($string);$i++){
$char_a=mb_substr($string,$i,1);
$char_b=mb_substr($string,$i+1,1);
$a=abs($keys[$char_a][0]-$keys[$char_b][0]);
$b=abs($keys[$char_a][1]-$keys[$char_b][1]);
$distance=sqrt($a^2+$b^2);
$distances[]=$distance;
}
return array_sum($distances)/count($distances);
}
You can use it the following way.
string_distance('Boat'); # output 2.0332570942187
string_distance('HDxtaBQrGkjny'); # output 1.4580596252044
I used multibyte functions because I was thinking about extending it for other characters. One could extend it by checking the case of characters as well.

Categories