Is there a way to cut up a string by whitespace? - rust

I'm trying to automatically split up a string by line, and then by whitespace for use in a Vec of Vec3.
v 0.500000 0.500000 -0.500000
v 0.500000 -0.500000 -0.500000
v 0.500000 0.500000 0.500000
v 0.500000 -0.500000 0.500000
v -0.500000 0.500000 -0.500000
v -0.500000 -0.500000 -0.500000
v -0.500000 0.500000 0.500000
v -0.500000 -0.500000 0.500000
vt 0.875000 0.500000
vt 0.625000 0.750000
vt 0.625000 0.500000
For example, given this sample, I would want to discard lines starting with "vt", and store the coordinates from the lines starting with "v".

You can use str::split_whitespace(). Here's a simple parser. It does not handle errors (instead panics), but it should be a good starting point:
#[derive(Debug)]
struct Vec3(f32, f32, f32);
fn main() {
let str = "v 0.500000 0.500000 -0.500000
v 0.500000 -0.500000 -0.500000
v 0.500000 0.500000 0.500000
v 0.500000 -0.500000 0.500000
v -0.500000 0.500000 -0.500000
v -0.500000 -0.500000 -0.500000
v -0.500000 0.500000 0.500000
v -0.500000 -0.500000 0.500000
vt 0.875000 0.500000
vt 0.625000 0.750000
vt 0.625000 0.500000";
let parsed = str
.split("\n")
.filter_map(|line| {
let mut parts = line.split_whitespace();
if parts.next().unwrap() == "v" {
Some(Vec3(
parts.next().unwrap().parse().unwrap(),
parts.next().unwrap().parse().unwrap(),
parts.next().unwrap().parse().unwrap(),
))
} else {
None
}
})
.collect::<Vec<Vec3>>();
dbg!(parsed);
}
Output:
[src/main.rs:33] parsed = [
Vec3(
0.5,
0.5,
-0.5,
),
Vec3(
0.5,
-0.5,
-0.5,
),
Vec3(
0.5,
0.5,
0.5,
),
Vec3(
0.5,
-0.5,
0.5,
),
Vec3(
-0.5,
0.5,
-0.5,
),
Vec3(
-0.5,
-0.5,
-0.5,
),
Vec3(
-0.5,
0.5,
0.5,
),
Vec3(
-0.5,
-0.5,
0.5,
),
]
Playground

Between the use of lines and split_whitespace, you can step through both of your iterators to do something like this to collect each row into its own vec3:
const data: &'static str = r"v 0.500000 0.500000 -0.500000
v 0.500000 -0.500000 -0.500000
v 0.500000 0.500000 0.500000
v 0.500000 -0.500000 0.500000
v -0.500000 0.500000 -0.500000
v -0.500000 -0.500000 -0.500000
v -0.500000 0.500000 0.500000
v -0.500000 -0.500000 0.500000
vt 0.875000 0.500000
vt 0.625000 0.750000
vt 0.625000 0.500000";
fn main() {
for row in data.lines() {
let mut cols = row.split_whitespace();
if Some("vt") != cols.next() {
let vec3 = (
cols.next().unwrap().parse::<f32>().unwrap(),
cols.next().unwrap().parse::<f32>().unwrap(),
cols.next().unwrap().parse::<f32>().unwrap(),
);
println!("{:?}", vec3);
}
}
}
You can simplify that a bit by using a crate to unpack the iterator into a tuple without needing to touch each element: Is there any way to unpack an iterator into a tuple?

Related

Rescale pandas column based on a value within that column?

I'm trying to normalize a column of data to 1 based on an internal standard control across several batches of data. However, I'm struggling to do this natively in pandas and not splitting things into multiple chunks with for loops.
import pandas as pd
Test_Data = {"Sample":["Control","Test1","Test2","Test3","Test4","Control","Test1","Test2","Test3","Test4"],
"Batch":["A","A","A","A","A","B","B","B","B","B"],
"Input":[0.1,0.15,0.08,0.11,0.2,0.15,0.1,0.04,0.11,0.2],
"Output":[0.1,0.6,0.08,0.22,0.01,0.08,0.22,0.02,0.13,0.004]}
DB = pd.DataFrame(Test_Data)
DB.loc[:,"Ratio"] = DB["Output"]/DB["Input"]
DB:
Sample Batch Input Output Ratio
0 Control A 0.10 0.100 1.000000
1 Test1 A 0.15 0.600 4.000000
2 Test2 A 0.08 0.080 1.000000
3 Test3 A 0.11 0.220 2.000000
4 Test4 A 0.20 0.010 0.050000
5 Control B 0.15 0.080 0.533333
6 Test1 B 0.10 0.220 2.200000
7 Test2 B 0.04 0.020 0.500000
8 Test3 B 0.11 0.130 1.181818
9 Test4 B 0.20 0.004 0.020000
My desired output would be to normalize each ratio per Batch based on the Control sample, effectively multiplying all the Batch "B" samples by 1.875.
DB:
Sample Batch Input Output Ratio Norm_Ratio
0 Control A 0.10 0.100 1.000000 1.000000
1 Test1 A 0.15 0.600 4.000000 4.000000
2 Test2 A 0.08 0.080 1.000000 1.000000
3 Test3 A 0.11 0.220 2.000000 2.000000
4 Test4 A 0.20 0.010 0.050000 0.050000
5 Control B 0.15 0.080 0.533333 1.000000
6 Test1 B 0.10 0.220 2.200000 4.125000
7 Test2 B 0.04 0.020 0.500000 0.937500
8 Test3 B 0.11 0.130 1.181818 2.215909
9 Test4 B 0.20 0.004 0.020000 0.037500
I can do this by breaking up the dataframe using for loops and manually extracting the "Control" values, but this is slow and messy for large datasets.
Use where and groupby.transform:
DB['Norm_Ratio'] = DB['Ratio'].div(
DB['Ratio'].where(DB['Sample'].eq('Control'))
.groupby(DB['Batch']).transform('first')
)
Output:
Sample Batch Input Output Ratio Norm_Ratio
0 Control A 0.10 0.100 1.000000 1.000000
1 Test1 A 0.15 0.600 4.000000 4.000000
2 Test2 A 0.08 0.080 1.000000 1.000000
3 Test3 A 0.11 0.220 2.000000 2.000000
4 Test4 A 0.20 0.010 0.050000 0.050000
5 Control B 0.15 0.080 0.533333 1.000000
6 Test1 B 0.10 0.220 2.200000 4.125000
7 Test2 B 0.04 0.020 0.500000 0.937500
8 Test3 B 0.11 0.130 1.181818 2.215909
9 Test4 B 0.20 0.004 0.020000 0.037500

Assign different colors to polydata in paraview

Trying to avoid defining multiple individual polygons/quad, so I use polydata.
I need to define multiple polydata in a Matlab generated vtk file, but each one should be assigned a different color (defined in a lookup table).
The following code gives an error and accepts only the first color which it assigns to all polydata.
# vtk DataFile Version 5.1
vtk output
ASCII
DATASET POLYDATA
POINTS 12 float
0.500000 1.000000 0.000000
0.353553 1.000000 -0.353553
0.000000 1.000000 -0.500000
-0.353553 1.000000 -0.353553
-0.500000 1.000000 0.000000
-0.353553 1.000000 0.353553
0.000000 1.000000 0.500000
0.353553 1.000000 0.353553
0. 0. 0.
1. 1. 1.
2. 2. 2.
1. 2. 1.
POLYGONS 3 12
OFFSETS vtktypeint64
0 8 12
CONNECTIVITY vtktypeint64
0 1 2 3 4 5 6 7
9 10 11 12
CELL_DATA 2
SCALARS SMEARED float 1
LOOKUP_TABLE victor
0 1
LOOKUP_TABLE victor 1
1.000000 0.000000 0.000000 1.000000
0.000000 1.000000 0.000000 1.000000
LOOKUP_TABLE victor 1
This should be LOOKUP_TABLE victor 2, as you define 2 RGBA points in your table

data manipulation in pandas

I have a data like below:
col1 col2
A 0
A 1
B 0
A 0
C 1
B 1
C 0
I like it as below:
col1 col2 col3 col4
A 0 .33 .33
A 1 .33 .33
B 0 .5 .33
A 0 .33 .33
C 1 .5 .33
B 1 .5 .33
C 0 .5 .33
Col 1 are categories of values. Col 2 are events,i.e 0=no, 1=yes.
col 3 should be the event rate of the category,i.e
(number of times the category has value 1/total number of occurances of that category)
col 4 should be event share of the category,i.e,
(number of times the category has value1/total number of 1s across all categories,
e.g col 4 for A should be number of 1s in A divided by number of total 1s across categories A,B & C together.)
Can anyone please help
Use GroupBy.transform mean and sum, for second divide by sum of all 1 of df['col2']:
df['col3'] = df.groupby('col1')['col2'].transform('mean')
df['col4'] = df.groupby('col1')['col2'].transform('sum').div(df['col2'].sum())
print (df)
col1 col2 col3 col4
0 A 0 0.333333 0.333333
1 A 1 0.333333 0.333333
2 B 0 0.500000 0.333333
3 A 0 0.333333 0.333333
4 C 1 0.500000 0.333333
5 B 1 0.500000 0.333333
6 C 0 0.500000 0.333333
Another solution with aggregate by GroupBy.agg:
df = df.join(df.groupby('col1')['col2'].agg([('col3','mean'),('col4','sum')])
.assign(col4 = lambda x: x['col4'].div(df['col2'].sum())), on='col1')
print (df)
col1 col2 col3 col4
0 A 0 0.333333 0.333333
1 A 1 0.333333 0.333333
2 B 0 0.500000 0.333333
3 A 0 0.333333 0.333333
4 C 1 0.500000 0.333333
5 B 1 0.500000 0.333333
6 C 0 0.500000 0.333333

How to add path to texture in OBJ or MTL file?

I have next problem:
My project consists of .obj file, .mtl file and texture(.jpg).
I need to divide texture into multiple files. But, when I do it, the UV coordinates (after mapping and reverse mapping) will be the same on several files, thus it cause error watching obj using meshlab.
How can I solve my problem ?
Meshlab does support files with several texture files, just by using a separate material for each texture. It is not clear if you are generating your obj files with meshlab or other program, so I'm not sure if this is a meshlab related question.
Here is a sample of a minimal multitexture .obj file (8 vertex, 4 triangles, 2 textures)
mtllib ./TextureDouble.obj.mtl
# 8 vertices, 8 vertices normals
vn 0.000000 0.000000 1.570796
v 0.000000 0.000000 0.000000
vn 0.000000 0.000000 1.570796
v 1.000000 0.000000 0.000000
vn 0.000000 0.000000 1.570796
v 1.000000 1.000000 0.000000
vn 0.000000 0.000000 1.570796
v 0.000000 1.000000 0.000000
vn 0.000000 0.000000 1.570796
v 2.000000 0.000000 0.000000
vn 0.000000 0.000000 1.570796
v 3.000000 0.000000 0.000000
vn 0.000000 0.000000 1.570796
v 3.000000 1.000000 0.000000
vn 0.000000 0.000000 1.570796
v 2.000000 1.000000 0.000000
# 4 coords texture
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
# 2 faces using material_0
usemtl material_0
f 1/1/1 2/2/2 3/3/3
f 1/1/1 3/3/3 4/4/4
# 4 coords texture
vt 0.000000 0.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 1.000000
# 2 faces using material_1
usemtl material_1
f 5/5/5 6/6/6 7/7/7
f 5/5/5 7/7/7 8/8/8
And here is the TextureDouble.obj.mtl file. To test the files, you must provide 2 image files named TextureDouble_A.png and TextureDouble_B.png.
newmtl material_0
Ka 0.200000 0.200000 0.200000
Kd 1.000000 1.000000 1.000000
Ks 1.000000 1.000000 1.000000
Tr 1.000000
illum 2
Ns 0.000000
map_Kd TextureDouble_A.png
newmtl material_1
Ka 0.200000 0.200000 0.200000
Kd 1.000000 1.000000 1.000000
Ks 1.000000 1.000000 1.000000
Tr 1.000000
illum 2
Ns 0.000000
map_Kd TextureDouble_B.png

How to correctly format NURBS curves for wavefront .OBJ file format?

I am trying to write a wavefront .OBJ file that contains 3D NURBS curves (not surfaces). I found the following link that describes how to correctly format curves and surfaces within .OBJ files: http://www.martinreddy.net/gfx/3d/OBJ.spec
There is no example for a rational b-spline curve, and it's not clear to me from the documentation how the knot vector is formatted within the parm u section. Any help would be appreciated.
Examples of related code follow. At the link above, there is a description of a rational b-spline surface:
v -1.3 -1.0 0.0
v 0.1 -1.0 0.4 7.6
v 1.4 -1.0 0.0 2.3
v -1.4 0.0 0.2
v 0.1 0.0 0.9 0.5
v 1.3 0.0 0.4 1.5
v -1.4 1.0 0.0 2.3
v 0.1 1.0 0.3 6.1
v 1.1 1.0 0.0 3.3
vt 0.0 0.0
vt 0.5 0.0
vt 1.0 0.0
vt 0.0 0.5
vt 0.5 0.5
vt 1.0 0.5
vt 0.0 1.0
vt 0.5 1.0
vt 1.0 1.0
cstype rat bspline
deg 2 2
surf 0.0 1.0 0.0 1.0 1/1 2/2 3/3 4/4 5/5 6/6 \
7/7 8/8 9/9
parm u 0.0 0.0 0.0 1.0 1.0 1.0
parm v 0.0 0.0 0.0 1.0 1.0 1.0
end
and another example for a bezier curve:
v -2.300000 1.950000 0.000000
v -2.200000 0.790000 0.000000
v -2.340000 -1.510000 0.000000
v -1.530000 -1.490000 0.000000
v -0.720000 -1.470000 0.000000
v -0.780000 0.230000 0.000000
v 0.070000 0.250000 0.000000
v 0.920000 0.270000 0.000000
v 0.800000 -1.610000 0.000000
v 1.620000 -1.590000 0.000000
v 2.440000 -1.570000 0.000000
v 2.690000 0.670000 0.000000
v 2.900000 1.980000 0.000000
# 13 vertices
cstype bezier
ctech cparm 1.000000
deg 3
curv 0.000000 4.000000 1 2 3 4 5 6 7 8 9 10 \
11 12 13
parm u 0.000000 1.000000 2.000000 3.000000 \
4.000000
end
# 1 element
There are multiple ways to store the information of a NURBS curve in the wavefront .obj file.
Here is one example:
v -2.300000 1.950000 1.000000 1.000000
v -2.200000 0.790000 2.000000 1.000000
v -2.340000 -1.510000 0.000000 1.000000
v -1.530000 -1.490000 0.000000 1.000000
v -0.720000 -1.470000 0.000000 1.000000
v -0.780000 0.230000 0.000000 1.000000
cstype rat bspline
deg 2
curv 0.00 1.00 1 2 3 4 5 6
parm u 0.00 0.00 0.00 0.25 0.50 0.75 1.00 1.00 1.00
end
Now let's have a closer look. We have 6 vertices in cartesian coordinates with additional weight coordinate (x, y, z, w). To define a rational b-spline (NURBS) with a degree of 2 we have to set
cstype rat bspline
deg 2
The next values are defining the curv. The syntax is:
curv [u-start] [u-end] [first-cp] [second-cp] [...]
http://www.martinreddy.net/gfx/3d/OBJ.spec, line 788:
curv u0 u1 v1 v2 . . .
Element statement for free-form geometry.
Specifies a curve, its parameter range, and its control vertices.
Although curves cannot be shaded or rendered, they are used by other
Advanced Visualizer programs.
u0 is the starting parameter value for the curve. This is a floating
point number.
u1 is the ending parameter value for the curve. This is a floating
point number.
v is the vertex reference number for a control point. You can specify
multiple control points. A minimum of two control points are required
for a curve.
For a non-rational curve, the control points must be 3D. For a
rational curve, the control points are 3D or 4D. The fourth coordinate
(weight) defaults to 1.0 if omitted.
Now we define the u vector/sequence. The values are of course depending on your geometry.
parm u [knot1] [knot2] [...]
http://www.martinreddy.net/gfx/3d/OBJ.spec, line 1107:
parm u p1 p2 p3. . .
parm v p1 p2 p3 . . .
Body statement for free-form geometry.
Specifies global parameter values. For B-spline curves and surfaces,
this specifies the knot vectors.
u is the u direction for the parameter values.
v is the v direction for the parameter values.
To set u and v values, use separate command lines.
p is the global parameter or knot value. You can specify multiple
values. A minimum of two parameter values are required. Parameter
values must increase monotonically. The type of surface and the degree
dictate the number of values required.
I hope this helps!

Resources