OpenCascade: Cylindrical Face with Boundary Wire - geometry-surface

I want to make the face of half a cylinder, using the boundary edges (two vertical lines and two 180° arcs). I know there are easier ways to do this, but my real problem is much more complex, and the edges I have are mostly splines. So, I tried to make a very simple example in hope someone might help me.
With my real input data, I do not know the order and orientation of the boundary edges. All I have is "cylindrical face" and "bucket of edges, that form a closed loop". So, if my orientation is bad, how can I automatically fix that?
Here's my example code:
// make an ARC from 'start' to 'end', counter-clockwise around 'center'.
TopoDS_Edge mkArc(gp_Pnt start, gp_Pnt center, gp_Pnt end, double normalZ) {
gp_Circ geometricCircle = GC_MakeCircle(center
, gp_Dir(0, 0, normalZ)
, center.Distance(start)
).Value()->Circ();
return BRepBuilderAPI_MakeEdge(geometricCircle, start, end);
}
// Make half-cylinder face by using boundary edges
TopoDS_Face MakeClinderFaceTest() {
// ^Z
// _
// ,´ `.
// a c b
// | _ |
// |,´ `.|
// A C B -->X
// top nodes
gp_Pnt a = gp_Pnt(-1, 0, 0);
gp_Pnt b = gp_Pnt( 1, 0, 0);
gp_Pnt c = gp_Pnt( 0, 0, 0);
// bottom nodes
gp_Pnt A = gp_Pnt(-1, 0, -1);
gp_Pnt B = gp_Pnt( 1, 0, -1);
gp_Pnt C = gp_Pnt( 0, 0, -1);
// boundary wire
std::list<TopoDS_Edge> edges;
if (0) { // 1/0 to reverse the order and direction of edges
edges.push_back(mkArc(a, c, b, -1)); // top arc
edges.push_back(BRepBuilderAPI_MakeEdge(b, B)); // right line
edges.push_back(mkArc(B, C, A, 1)); // bottom arc
edges.push_back(BRepBuilderAPI_MakeEdge(A, a)); // left line
} else {
edges.push_back(mkArc(b, c, a, 1));
edges.push_back(BRepBuilderAPI_MakeEdge(a, A));
edges.push_back(mkArc(A, C, B, -1));
edges.push_back(BRepBuilderAPI_MakeEdge(B, b));
}
BRepBuilderAPI_MakeWire wire;
for (auto& e : edges) {
wire.Add(e);
}
auto cylinder = gp_Cylinder( gp_Ax2( C, gp_Dir(0, 0, 1) ), C.Distance(A) /* =R */ );
#if 0
// surface geometry: infinite length cylinder
BRepBuilderAPI_MakeFace cylface(cylinder, wire);
#else
// cylindrical face with limits in V direction.
TopoDS_Face cylinder_face = BRepBuilderAPI_MakeFace(cylinder, 0, 2 * M_PI, 0, 1.0).Face();
// Limit cylinder by wired edges
BRepBuilderAPI_MakeFace cylface(cylinder_face, wire);
#endif
return cylface;
}

It is always better performing direct modeling - e.g. constructing vertices, edges, wires, faces in reliable way defining a valid topology.
But considering the question:
So, if my orientation is bad, how can I automatically fix that?
The shape healing services are provided by ShapeFix package in Open CASCADE Technology. It's main purpose is solving topology issues on shapes imported from external CAD systems (applying different criteria of validity or just writing an invalid shape into STEP / IGES file).
These tools might be also used for making a 'lazy' building algorithm or for handling user input. Beware, that tools provides a lot of 'fixes' - e.g. algorithms solving particular kind of issue. Enabling all of them is not a good idea as it will dramatically affect performance and result might be unexpected.
In your particular case, you are interested in fixing wire orientation. This can be achieved with help of ShapeFix_Face like this (if I understand your question correctly):
// cylindrical face with limits in V direction.
TopoDS_Face cylinder_face = BRepBuilderAPI_MakeFace(cylinder, 0, 2 * M_PI, 0, 1.0).Face();
Handle(Geom_Surface) aSurf = BRep_Tool::Surface (cylinder_face);
// Limit cylinder by wired edges
//BRepBuilderAPI_MakeFace cylface(cylinder_face, wire);
BRepBuilderAPI_MakeFace cylface(aSurf, wire);
ShapeFix_Face aFaceFixer;
aFaceFixer.FixWireMode() = 1;
aFaceFixer.FixOrientationMode() = 1;
//aFaceFixer.FixSplitFaceMode() = 1;
Handle(ShapeFix_Wire) aWireFixer = aFaceFixer.FixWireTool();
aWireFixer->FixConnectedMode() = 1;
aWireFixer->ClosedWireMode() = Standard_True;
Handle(ShapeBuild_ReShape) aContext = new ShapeBuild_ReShape();
aFaceFixer.SetContext (aContext);
aFaceFixer.Init (cylface);
aFaceFixer.Perform();
TopoDS_Shape aFixResult = aFaceFixer.Result();
if (!aFixResult.IsNull()) { return TopoDS::Face (aFixResult); }

Related

How to "move" or "traverse" the hyperbolic tessellation in MagicTile?

Alright I think I've mostly figured out how the MagicTile works, the source code at least (not really the Math as much yet). It all begins with the build and render calls in the MainForm.cs. It generates a tessellation like this:
First, it "generates" the tessellation. Since MagicTile is a Rubic's cube-like game, I guess it just statically computes all of the tiles up front. It does this by starting with a central tile, and reflecting its polygon (and the polygon's segments and points) using some sort of math which I've read about several times but I couldn't explain. Then it appears they allow rotations of the tessellation, where they call code like this in the "renderer":
Polygon p = sticker.Poly.Clone();
p.Transform( m_mouseMotion.Isometry );
Color color = GetStickerColor( sticker );
GLUtils.DrawConcavePolygon( p, color, GrabModelTransform() );
They track the mouse position, like if you are dragging, and somehow that is used to create an "isometry" to augment / transform the overall tessellation. So then we transform the polygon using that isometry. _It appears they only do the central tile and 1 or 2 levels after that, but I can't quite tell, I haven't gotten the app to run and debug yet (it's also in C# and that's a new language for me, coming from TypeScript). The Transform function digs down like this (here it is in TypeScript, as I've been converting it):
TransformIsometry(isometry: Isometry) {
for (let s of this.Segments) {
s.TransformIsometry(isometry)
}
this.Center = isometry.Apply(this.Center)
}
That goes into the transform for the segments here:
/// <summary>
/// Apply a transform to us.
/// </summary>
TransformInternal<T extends ITransform>(transform: T) {
// NOTES:
// Arcs can go to lines, and lines to arcs.
// Rotations may reverse arc directions as well.
// Arc centers can't be transformed directly.
// NOTE: We must calc this before altering the endpoints.
let mid: Vector3D = this.Midpoint
if (UtilsInfinity.IsInfiniteVector3D(mid)) {
mid = this.P2.MultiplyWithNumber(UtilsInfinity.FiniteScale)
}
mid = UtilsInfinity.IsInfiniteVector3D(this.P1)
? this.P2.MultiplyWithNumber(UtilsInfinity.FiniteScale)
: this.P1.MultiplyWithNumber(UtilsInfinity.FiniteScale)
this.P1 = transform.ApplyVector3D(this.P1)
this.P2 = transform.ApplyVector3D(this.P2)
mid = transform.ApplyVector3D(mid)
// Can we make a circle out of the transformed points?
let temp: Circle = new Circle()
if (
!UtilsInfinity.IsInfiniteVector3D(this.P1) &&
!UtilsInfinity.IsInfiniteVector3D(this.P2) &&
!UtilsInfinity.IsInfiniteVector3D(mid) &&
temp.From3Points(this.P1, mid, this.P2)
) {
this.Type = SegmentType.Arc
this.Center = temp.Center
// Work out the orientation of the arc.
let t1: Vector3D = this.P1.Subtract(this.Center)
let t2: Vector3D = mid.Subtract(this.Center)
let t3: Vector3D = this.P2.Subtract(this.Center)
let a1: number = Euclidean2D.AngleToCounterClock(t2, t1)
let a2: number = Euclidean2D.AngleToCounterClock(t3, t1)
this.Clockwise = a2 > a1
} else {
// The circle construction fails if the points
// are colinear (if the arc has been transformed into a line).
this.Type = SegmentType.Line
// XXX - need to do something about this.
// Turn into 2 segments?
// if( UtilsInfinity.IsInfiniteVector3D( mid ) )
// Actually the check should just be whether mid is between p1 and p2.
}
}
So as far as I can tell, this will adjust the segments based on the mouse position, somehow. Mouse position isometry updating code is here.
So it appears they don't have the functionality to "move" the tiling, like if you were walking on it, like in HyperRogue.
So after having studied this code for a few days, I am not sure how to move or walk along the tiles, moving the outer tiles toward the center, like you're a giant walking on Earth.
First small question, can you do this with MagicTile? Can you somehow update the tessellation to move a different tile to the center? (And have a function which I could plug a tween/animation into so it animates there). Or do I need to write some custom new code? If so, what do I need to do roughly speaking, maybe some pseudocode?
What I imagine is, user clicks on the outer part of the tessellation. We convert that click data to the tile index in the tessellation, then basically want to do tiling.moveToCenter(tile), but frame-by-frame-animation, so not quite sure how that would work. But that moveToCenter, what would that do in terms of the MagicTile rendering/tile-generating code?
As I described in the beginning, it first generates the full tessellation, then only updates 1-3 layers of the tiles for their puzzles. So it's like I need to first shift the frame of reference, and recompute all the potential visible tiles, somehow not recreating the ones that were already created. I don't quite see how that would work, do you? Once the tiles are recomputed, then I just re-render and it should show the updated center.
Is it a simple matter of calling some code like this again, for each tile, where the isometry is somehow updated with a border-ish position on the tessellation?
Polygon p = sticker.Poly.Clone();
p.Transform( m_mouseMotion.Isometry );
Or must I do something else? I can't quite see the full picture yet.
Or is that what these 3 functions are doing! TypeScript port of the C# MagicTile:
// Move from a point p1 -> p2 along a geodesic.
// Also somewhat from Don.
Geodesic(g: Geometry, p1: Complex, p2: Complex) {
let t: Mobius = Mobius.construct()
t.Isometry(g, 0, p1.Negate())
let p2t: Complex = t.ApplyComplex(p2)
let m2: Mobius = Mobius.construct()
let m1: Mobius = Mobius.construct()
m1.Isometry(g, 0, p1.Negate())
m2.Isometry(g, 0, p2t)
let m3: Mobius = m1.Inverse()
this.Merge(m3.Multiply(m2.Multiply(m1)))
}
Hyperbolic(g: Geometry, fixedPlus: Complex, scale: number) {
// To the origin.
let m1: Mobius = Mobius.construct()
m1.Isometry(g, 0, fixedPlus.Negate())
// Scale.
let m2: Mobius = Mobius.construct()
m2.A = new Complex(scale, 0)
m2.C = new Complex(0, 0)
m2.B = new Complex(0, 0)
m2.D = new Complex(1, 0)
// Back.
// Mobius m3 = m1.Inverse(); // Doesn't work well if fixedPlus is on disk boundary.
let m3: Mobius = Mobius.construct()
m3.Isometry(g, 0, fixedPlus)
// Compose them (multiply in reverse order).
this.Merge(m3.Multiply(m2.Multiply(m1)))
}
// Allow a hyperbolic transformation using an absolute offset.
// offset is specified in the respective geometry.
Hyperbolic2(
g: Geometry,
fixedPlus: Complex,
point: Complex,
offset: number,
) {
// To the origin.
let m: Mobius = Mobius.construct()
m.Isometry(g, 0, fixedPlus.Negate())
let eRadius: number = m.ApplyComplex(point).Magnitude
let scale: number = 1
switch (g) {
case Geometry.Spherical:
let sRadius: number = Spherical2D.e2sNorm(eRadius)
sRadius = sRadius + offset
scale = Spherical2D.s2eNorm(sRadius) / eRadius
break
case Geometry.Euclidean:
scale = (eRadius + offset) / eRadius
break
case Geometry.Hyperbolic:
let hRadius: number = DonHatch.e2hNorm(eRadius)
hRadius = hRadius + offset
scale = DonHatch.h2eNorm(hRadius) / eRadius
break
default:
break
}
this.Hyperbolic(g, fixedPlus, scale)
}

How to efficiently cover a set of points with circles when you can't access point coordinates? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed last month.
Improve this question
Suppose I have a finite set of points distributed in a unit square. I can't access the point coordinates; instead, I can only specify a (point, radius) pair and see how many points fall inside that circle. I want to find a set of circles such that each point is in at least one circle, and no circle contains more than 1000 points. What's an efficient way to do this? E.g. a way that minimizes the expected number of (point, radius) searches?
I tried a recursive approach. E.g. f(point, radius) takes a circle and returns a set of smaller circles that cover it. Then recurse until each circle contains fewer than 1000 points. But there's not a straightforward (to me) way to choose the smaller circles in the recursive step.
Edit: Circles are allowed to overlap with each other / with the outside of the square.
Not having a strict partition ("strict" - where the circles in the solution may not overlap and points must appear in exactly 1 circle) simplifies the problem.
The straight-forward way to subdivide a circle under those circumstances is to form a set of child circles that circumscribe the four quadrants of the parent...
Here's a (cursorily tested) demo using that approach
class Circle {
constructor(x,y,radius) {
Object.assign(this, { x, y, radius })
this.rSquared = radius*radius
}
contains(point) {
let dx = point.x - this.x
let dy = point.y - this.y
return dx*dx + dy*dy < this.rSquared
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
ctx.stroke();
}
subdivide() {
const halfR = this.radius / 2.0
const smallR = this.radius * Math.SQRT2 / 2.0
return [
new Circle(this.x-halfR, this.y-halfR, smallR),
new Circle(this.x-halfR, this.y+halfR, smallR),
new Circle(this.x+halfR, this.y-halfR, smallR),
new Circle(this.x+halfR, this.y+halfR, smallR),
]
}
}
// this class keeps a set of random points and answers countInCircle()
// solutions may only call countInCircle()
class Puzzler {
constructor(count) {
this.points = []
for (let i=0; i<count; i++) {
let point = { x: Math.random()*width, y: Math.random()*height}
this.points.push(point)
}
}
// answer how many points fall inside circle
countInCircle(circle) {
return this.points.reduce((total, p) => total += circle.contains(p) ? 1 : 0, 0);
}
drawSolution(circles) {
// draw the random points
this.points.map(p => ctx.fillRect(p.x,p.y,2,2))
// draw the circles in the solution
ctx.strokeStyle = 'lightgray'
circles.forEach(circle => circle.draw())
// log some stats - commented a few of these out for snippet brevity
const counts = circles.map(circle => this.countInCircle(circle));
console.log('circles:', circles.length)
// console.log('counts:', counts.join(', '))
// console.log('counts above 100:', counts.filter(c => c > 100).length)
const averageCount = counts.reduce((a, b) => a + b) / counts.length
console.log('average count:', averageCount.toFixed(2))
const uncovered = this.points.reduce((total, point) => {
return total + (circles.some(circle => circle.contains(point)) ? 0 : 1)
}, 0)
console.log('uncovered points:', uncovered)
}
}
// setup canvas
const canvas = document.getElementById('canvas')
const { width, height } = canvas
const ctx = canvas.getContext('2d')
// setup puzzle
const count = 1000
const maxCountPer = 100
const puzzler = new Puzzler(count, maxCountPer)
// begin with an encompasing circle, subdivide and solve recursively
// until all subdivided circles meet the count criterion
let r = width*Math.SQRT2/2
let c = new Circle(width/2.0, width/2.0, r)
let solution = solve(c);
function solve(circle) {
let result = []
let count = puzzler.countInCircle(circle)
if (count > 0 && count <= maxCountPer) {
result.push(circle);
} else if (count > maxCountPer) {
circle.subdivide().forEach(c => {
result.push(...solve(c))
})
}
return result
}
requestAnimationFrame(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
puzzler.drawSolution(solution)
});
<h1>Circles Puzzle</h1>
<canvas style="border: 1px solid gray;" id="canvas" height="800" width="800"></canvas>
Assumption: When you pick a point and a radius, you get back a list of points that are in the containing circle. I.e., you know which points are covered by which circles.
If that's correct,then you can map out the approximate relative location of all points, after which answers to this similar question should carry you over the finish line.
To map out the relative location of all points:
Note that you can find the distance between any pair of points by centering your circle on one and using binary search on your radius to find the distance to the other within whatever precision you want to use.
Next choose three arbitrary points that aren't too close together. Pick an arbitrary point. Grow the radius, say to 1/4. Pick an arbitrary point close to that radius (by incrementing radius a bit to get another point, or using binary search on radius). Say the distance between these first two points is d. Pick a third point at distance >= d from the first two points but ideally close to d, again by incrementing the two radii or binary search on the same.
Now you have a roughly equilateral triangle. It isn't important that it's equilateral, but it is important that the points aren't very close, and aren't co-linear.
Next, give these three points coordinates. Say the first point is at (0,0), the second point is at (0, dist to first point). The third point will have two possible locations based on its distance from the first two. Choose the one in the first quadrant (arbitrarily).
All other points can now be positioned relative to this triangle by finding their distance two the points of the triangle.
For purposes of your problem, it doesn't matter that the cloud of points is rotated relative to the input, or that we don't know where the unit square is relative to the points. You have a cloud of points with (approximately) known coordinates, and can proceed accordingly.

Shape appears to have less vertexes than shape data THREE.JS

I'm trying to create a smooth "wave" when the mouse moves over isometric logo shape.
I've created in in processing now I'm trying to recreate it in THREE.js
The shape acts strangely - the shape doesn't look as smooth when elevated compared to the processing sketch. If you look at the edges you can see segments that are not supposed to be there. I'm not sure what causes this.
Basically the shape is created through a loops that goes over 2 arrays:
for (var i = 0; i < xpos0.length; i++) {
shape.lineTo(xpos0[i], ypos0[i]);
}
Then it animates through another for loop that checks the distance between verteces[i].x and mouse location intersection with the ground
for (let p = 0; p < mesh.geometry.vertices.length; p=p+1) {
let delta = Math.abs(mesh.geometry.vertices[p].x - intersects[0].point.x);
mesh.geometry.vertices[p].z = bump2(-2, 2000, -1, 2, delta);
}
z value is calculated through this function:
function bump2(a,b,c,d,xval) {
xval = parseFloat(xval);
// console.log(typeof xval);
return Math.exp(a / (-xval * xval / b + c) + d) * -1;
}
https://codepen.io/NotYetDesignLab/pen/JjYaqRJ
How it looks on THREE.JS
notice how some segments appear "broken", like it's made of stiff parts instead of the many points that make up the segment in the array and give the illusion of "paper".
THIS IS HOW IT'S SUPPOSED TO LOOK: (Processing/java)
This has been done using Processing. Notice how the elevation of the edges is smooth and not broken.

OpenGL ES: Handle large amount matrixdata improve performance

I am using instancing in my OpenGL-app and since only one drawcall are made I have to calculate a larger matrix that consists of smaller matrices and that larger matrix is sent to the shader where gl_InstanceID can distinguish between successive matrices.
Its put on the bus with the following call
GLES30.glUniformMatrix4fv(mMVPMatrixHandleBall, nBalls, false, mMVPMatrixMajor, 0);
and in the shader the multiplication si made by
gl_Position = u_MVPMatrix[gl_InstanceID] * a_Position;
simple!
On the client-side the larger matrix is created by the following code:
private void setLargeMVPmatrix() {
int cnt = 0;
for (Iterator<Ball> shapeIterator = arrayListBalls.iterator(); shapeIterator.hasNext(); ) {
Ball ball = shapeIterator.next();
mModelMatrix = ball.getmModelMatrix();
//multipl.
Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
//subst. in matrisdata i en större vektor dvs vi får en stor matris som innehhåller flera mindre matriser
for (int i = 0; i < CreateGLContext.MATRIX_SIZE; i++) {
mMVPMatrixMajor[i + CreateGLContext.MATRIX_SIZE * cnt] = mMVPMatrix[i];
}
cnt++;
}
}
If I have moving-objects on the screen, like bouncing balls, for instance 100 balls bouncing around it means I have to continously translate their positions each frame which in turn means I have to call this method every frame. And the consequence is that it becomes a real performance bottelneck. I know it by just commenting out the method just to see what happends - and a real performance boost but the balls doesnt not move any longer, of course.
So my question - Is there a soluition to this problem? If I use instancing, I have to send a large matrix according to above.
Edit:
I've even tried the following which I thought could at least partially solve my problem. In the drawMethod:
int cnt = 0;
for (Iterator <Ball> it = arrayListBalls.iterator(); it.hasNext();) {
Ball ball = it.next();
mModelMatrix = ball.getmModelMatrix();
//multipl.
Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
GLES30.glUniformMatrix4fv( (mMVPMatrixHandleBall + cnt), 1, false, mMVPMatrix, 0);
cnt++;
}
Thanks in advance!!!
If the data that change are positions and rotations then that's what you should update to the shader.
Doing most of matrix stuff at CPU is slow, unless the needed operations are tiny, like computing the new view and projection matrices, same for all objects, and they are cheap to pass as uniforms
For every frame I'd re-fill a BufferData, perhaps with the help of glMapBufferRange or glBufferSubData, with the new positions and rotations.
Then, in the shader, build the matrices needed and do matrices multiplication there.
If initial positions and rotations are needed to build new matrices, then you must also pass them in another buffer, although just update it for the first frame.
With the proper attributes order you read in the shader these positions and rotations. The gl_InstanceID is then not needed for gl_Position calculus, perhaps needed for other object property.
If you need help on how to build matrices inside the shaders, look for glRotate and glTranslate in OpenGL 2.1 docs where you can find the definitions.
Also note that passing a big matrix for all objects by an uniform may exceed the limit on the size for the whole uniform data.

How to smooth the blocks of a 3D voxel world?

In my (Minecraft-like) 3D voxel world, I want to smooth the shapes for more natural visuals. Let's look at this example in 2D first.
Left is how the world looks without any smoothing. The terrain data is binary and each voxel is rendered as a unit size cube.
In the center you can see a naive circular smoothing. It only takes the four directly adjacent blocks into account. It is still not very natural looking. Moreover, I'd like to have flat 45-degree slopes emerge.
On the right you can see a smoothing algorithm I came up with. It takes the eight direct and diagonal neighbors into account in order to come up with the shape of a block. I have the C++ code online. Here is the code that comes up with the control points that the bezier curve is drawn along.
#include <iostream>
using namespace std;
using namespace glm;
list<list<dvec2>> Points::find(ivec2 block)
{
// Control points
list<list<ivec2>> lines;
list<ivec2> *line = nullptr;
// Fetch blocks, neighbours start top left and count
// around the center block clock wise
int center = m_blocks->get(block);
int neighs[8];
for (int i = 0; i < 8; i++) {
auto coord = blockFromIndex(i);
neighs[i] = m_blocks->get(block + coord);
}
// Iterate over neighbour blocks
for (int i = 0; i < 8; i++) {
int current = neighs[i];
int next = neighs[(i + 1) % 8];
bool is_side = (((i + 1) % 2) == 0);
bool is_corner = (((i + 1) % 2) == 1);
if (line) {
// Border between air and ground needs a line
if (current != center) {
// Sides are cool, but corners get skipped when they don't
// stop a line
if (is_side || next == center)
line->push_back(blockFromIndex(i));
} else if (center || is_side || next == center) {
// Stop line since we found an end of the border. Always
// stop for ground blocks here, since they connect over
// corners so there must be open docking sites
line = nullptr;
}
} else {
// Start a new line for the border between air and ground that
// just appeared. However, corners get skipped if they don't
// end a line.
if (current != center) {
lines.emplace_back();
line = &lines.back();
line->push_back(blockFromIndex(i));
}
}
}
// Merge last line with first if touching. Only close around a differing corner for air
// blocks.
if (neighs[7] != center && (neighs[0] != center || (!center && neighs[1] != center))) {
// Skip first corner if enclosed
if (neighs[0] != center && neighs[1] != center)
lines.front().pop_front();
if (lines.size() == 1) {
// Close circle
auto first_point = lines.front().front();
lines.front().push_back(first_point);
} else {
// Insert last line into first one
lines.front().insert(lines.front().begin(), line->begin(), line->end());
lines.pop_back();
}
}
// Discard lines with too few points
auto i = lines.begin();
while (i != lines.end()) {
if (i->size() < 2)
lines.erase(i++);
else
++i;
}
// Convert to concrete points for output
list<list<dvec2>> points;
for (auto &line : lines) {
points.emplace_back();
for (auto &neighbour : line)
points.back().push_back(pointTowards(neighbour));
}
return points;
}
glm::ivec2 Points::blockFromIndex(int i)
{
// Returns first positive representant, we need this so that the
// conditions below "wrap around"
auto modulo = [](int i, int n) { return (i % n + n) % n; };
ivec2 block(0, 0);
// For two indices, zero is right so skip
if (modulo(i - 1, 4))
// The others are either 1 or -1
block.x = modulo(i - 1, 8) / 4 ? -1 : 1;
// Other axis is same sequence but shifted
if (modulo(i - 3, 4))
block.y = modulo(i - 3, 8) / 4 ? -1 : 1;
return block;
}
dvec2 Points::pointTowards(ivec2 neighbour)
{
dvec2 point;
point.x = static_cast<double>(neighbour.x);
point.y = static_cast<double>(neighbour.y);
// Convert from neighbour space into
// drawing space of the block
point *= 0.5;
point += dvec2(.5);
return point;
}
However, this is still in 2D. How to translate this algorithm into three dimensions?
You should probably have a look at the marching cubes algorithm and work from there. You can easily control the smoothness of the resulting blob:
Imagine that each voxel defines a field, with a high density at it's center, slowly fading to nothing as you move away from the center. For example, you could use a function that is 1 inside a voxel and goes to 0 two voxels away. No matter what exact function you choose, make sure that it's only non-zero inside a limited (preferrably small) area.
For each point, sum the densities of all fields.
Use the marching cubes algorithm on the sum of those fields
Use a high resolution mesh for the algorithm
In order to change the look/smoothness you change the density function and the threshold of the marching cubes algorithm. A possible extension to marching cubes to create smoother meshes is the following idea: Imagine that you encounter two points on an edge of a cube, where one point lies inside your volume (above a threshold) and the other outside (under the threshold). In this case many marching cubes algorithms place the boundary exactly at the middle of the edge. One can calculate the exact boundary point - this gets rid of aliasing.
Also I would recommend that you run a mesh simplification algorithm after that. Using marching cubes results in meshes with many unnecessary triangles.
As an alternative to my answer above: You could also use NURBS or any algorithm for subdivision surfaces. Especially the subdivision surfaces algorithms are spezialized to smooth meshes. Depending on the algorithm and it's configuration you will get smoother versions of your original mesh with
the same volume
the same surface
the same silhouette
and so on.
Use 3D implementations for Biezer curves known as Biezer surfaces or use the B-Spline Surface algorithms explained:
here
or
here

Resources