|
19 | 19 |
|
20 | 20 | __all__ = [
|
21 | 21 | 'extrans', 'extrusion', 'revolution', 'helix', 'screw', 'saddle', 'tube',
|
22 |
| - 'repeat', 'repeataround', 'thicken', 'inflate', 'inflate_offsets', 'expand', |
| 22 | + 'repeat', 'repeataround', |
23 | 23 | 'flatsurface', 'icosurface',
|
24 | 24 | 'square', 'brick', 'parallelogram', 'cylinder', 'cone', 'pyramid', 'icosahedron', 'icosphere', 'uvsphere', 'regon',
|
25 | 25 | ]
|
@@ -318,197 +318,6 @@ def extrans(section, transformations:iter, links=None) -> Mesh:
|
318 | 318 |
|
319 | 319 | return mesh
|
320 | 320 |
|
321 |
| -def linstep(start, stop, x): |
322 |
| - if x <= start: return 0 |
323 |
| - if x >= stop: return 1 |
324 |
| - return (x-start)/(stop-start) |
325 |
| - |
326 |
| -def inflate_offsets(surface: Mesh, offset: float, method='face') -> '[vec3]': |
327 |
| - ''' Displacements vectors for points of a surface we want to inflate. |
328 |
| - |
329 |
| - Parameters: |
330 |
| - offset: |
331 |
| - the distance from the surface to the offset surface. Its meaning depends on `method` |
332 |
| - method: |
333 |
| - determines if the distance is from the old to the new faces, edges or points |
334 |
| - possible values: `'face', 'edge', 'point'` |
335 |
| - ''' |
336 |
| - pnormals = surface.vertexnormals() |
337 |
| - |
338 |
| - # smooth normal offsets laterally when they are closer than `offset` |
339 |
| - outlines = surface.outlines_oriented() |
340 |
| - l = len(pnormals) |
341 |
| - normals = deepcopy(pnormals) |
342 |
| - for i in range(5): # the number of steps is the diffusion distance through the mesh |
343 |
| - for a,b in outlines: |
344 |
| - d = surface.points[a] - surface.points[b] # edge direction |
345 |
| - t = cross(pnormals[a]+pnormals[b], d) # surface tangent normal to the edge |
346 |
| - # contribution stars when the offset points are closer than `offset` |
347 |
| - contrib = 1 - smoothstep(0, offset, length(offset*(pnormals[a]-pnormals[b])+d)) |
348 |
| - normals[a] += contrib * 0.5*project(pnormals[b]-pnormals[a], t) |
349 |
| - normals[b] += contrib * 0.5*project(pnormals[a]-pnormals[b], t) |
350 |
| - # renormalize |
351 |
| - for i in range(l): |
352 |
| - pnormals[i] = normals[i] = normalize(normals[i]) |
353 |
| - |
354 |
| - # compute offset length depending on the method |
355 |
| - if method == 'face': |
356 |
| - lengths = [inf]*len(pnormals) |
357 |
| - for face in surface.faces: |
358 |
| - fnormal = surface.facenormal(face) |
359 |
| - for p in face: |
360 |
| - lengths[p] = min(lengths[p], 1/dot(pnormals[p], fnormal)) |
361 |
| - return typedlist((pnormals[p]*lengths[p]*offset for p in range(len(pnormals))), dtype=vec3) |
362 |
| - |
363 |
| - elif method == 'edge': |
364 |
| - lengths = [inf]*len(pnormals) |
365 |
| - for edge,enormal in surface.edgenormals().items(): |
366 |
| - for p in edge: |
367 |
| - lengths[p] = min(lengths[p], 1/dot(pnormals[p], enormal)) |
368 |
| - return typedlist((pnormals[p]*lengths[p]*offset for p in range(len(pnormals))), dtype=vec3) |
369 |
| - |
370 |
| - elif method == 'point': |
371 |
| - return typedlist((pnormals[p]*offset for p in range(len(pnormals))), dtype=vec3) |
372 |
| - |
373 |
| -def inflate(surface:Mesh, offset:float, method='face') -> 'Mesh': |
374 |
| - ''' Move all points of the surface to make a new one at a certain distance of the last one |
375 |
| -
|
376 |
| - Parameters: |
377 |
| - offset: the distance from the surface to the offseted surface. its meaning depends on `method` |
378 |
| - method: determines if the distance is from the old to the new faces, edges or points |
379 |
| - ''' |
380 |
| - return Mesh( |
381 |
| - typedlist((p+d for p,d in zip(surface.points, inflate_offsets(surface, offset, method))), dtype=vec3), |
382 |
| - surface.faces, |
383 |
| - surface.tracks, |
384 |
| - surface.groups) |
385 |
| - |
386 |
| -def thicken(surface: Mesh, thickness: float, alignment:float=0, method='face') -> 'Mesh': |
387 |
| - ''' Thicken a surface by extruding it, points displacements are made along normal. |
388 |
| -
|
389 |
| - Parameters: |
390 |
| - thickness: determines the distance between the two surfaces (can be negative to go the opposite direction to the normal). |
391 |
| - alignment: specifies which side is the given surface: 0 is for the first, 1 for the second side, 0.5 thicken all apart the given surface. |
392 |
| - method: determines if the thickness is from the old to the new faces, edges or points |
393 |
| - ''' |
394 |
| - displts = inflate_offsets(surface, thickness, method) |
395 |
| - |
396 |
| - a = alignment |
397 |
| - b = alignment-1 |
398 |
| - m = ( Mesh( |
399 |
| - typedlist((p+d*a for p,d in zip(surface.points,displts)), dtype=vec3), |
400 |
| - surface.faces[:], |
401 |
| - surface.tracks[:], |
402 |
| - surface.groups) |
403 |
| - + Mesh( |
404 |
| - typedlist((p+d*b for p,d in zip(surface.points,displts)), dtype=vec3), |
405 |
| - surface.faces, |
406 |
| - surface.tracks, |
407 |
| - surface.groups[:] |
408 |
| - ) .flip() |
409 |
| - ) |
410 |
| - t = len(m.groups) |
411 |
| - l = len(surface.points) |
412 |
| - m.groups.append(None) |
413 |
| - for e in surface.outlines_oriented(): |
414 |
| - mkquad(m, (e[0], e[1], e[1]+l, e[0]+l), t) |
415 |
| - return m |
416 |
| - |
417 |
| - |
418 |
| -def expand(surface: Mesh, offset: float, collapse=True) -> Mesh: |
419 |
| - ''' Generate a surface expanding the input mesh on the tangent of the ouline neighboring faces |
420 |
| - |
421 |
| - Parameters: |
422 |
| - offset: distance from the outline point to the expanded outline points |
423 |
| - collapse: if True, expanded points leading to crossing edges will collapse into one |
424 |
| - ''' |
425 |
| - # outline with associated face normals |
426 |
| - pts = surface.points |
427 |
| - edges = {} |
428 |
| - for face in surface.faces: |
429 |
| - for e in ((face[0], face[1]), (face[1], face[2]), (face[2],face[0])): |
430 |
| - if e in edges: del edges[e] |
431 |
| - else: edges[(e[1], e[0])] = surface.facenormal(face) |
432 |
| - |
433 |
| - # return the point on tangent for a couple of edges from the frontier |
434 |
| - def tangent(e0, e1): |
435 |
| - mid = axis_midpoint( |
436 |
| - (pts[e0[1]], pts[e0[0]] - pts[e0[1]]), |
437 |
| - (pts[e1[0]], pts[e1[1]] - pts[e1[0]]), |
438 |
| - ) |
439 |
| - d0 = pts[e0[1]] - pts[e0[0]] |
440 |
| - d1 = pts[e1[1]] - pts[e1[0]] |
441 |
| - n0, n1 = edges[e0], edges[e1] |
442 |
| - t = normalize(cross(n0, n1) + NUMPREC*(d0*length(d1)-d1*length(d0)) + NUMPREC**2 * (cross(n0, d0) + cross(n1, d1))) |
443 |
| - if dot(t, cross(n0, d0)) < 0: |
444 |
| - t = -t |
445 |
| - return mid + t * offset |
446 |
| - |
447 |
| - # cross neighbooring normals |
448 |
| - for loop in suites(edges, cut=False): |
449 |
| - assert loop[-1] == loop[0], "non-manifold input mesh" |
450 |
| - loop.pop() |
451 |
| - # compute the offsets, and remove anticipated overlapping geometries |
452 |
| - extended = [None]*len(loop) |
453 |
| - for i in range(len(loop)): |
454 |
| - # consecutive edges around i-1 |
455 |
| - ei0 = (loop[i-2], loop[i-1]) |
456 |
| - ei1 = (loop[i-1], loop[i]) |
457 |
| - ti = tangent(ei0, ei1) |
458 |
| - if collapse: |
459 |
| - tk = deepcopy(ti) |
460 |
| - weight = 1 |
461 |
| - # j is moving to find how much points to gather |
462 |
| - ej0 = ei0 |
463 |
| - for j in reversed(range(i-len(loop)+1, i-1)): |
464 |
| - # consecutive edges aroung j |
465 |
| - ej0, ej1 = (loop[j-1], loop[j]), ej0 |
466 |
| - tj = tangent(ej0, ej1) |
467 |
| - |
468 |
| - if dot(ti - tj, pts[ei1[0]] - pts[ej0[1]]) <= NUMPREC * length2(pts[ei1[0]] - pts[ej0[1]]): |
469 |
| - tk += tj |
470 |
| - weight += 1 |
471 |
| - else: |
472 |
| - break |
473 |
| - # store tangents |
474 |
| - for k in range(j+1, i): |
475 |
| - extended[k] = tk/weight |
476 |
| - else: |
477 |
| - extended[i-1] = ti |
478 |
| - |
479 |
| - # insert the new points |
480 |
| - j = l = len(pts) |
481 |
| - g = len(surface.groups) |
482 |
| - surface.groups.append(None) |
483 |
| - for i in range(len(extended)): |
484 |
| - if extended[i] != extended[i-1]: |
485 |
| - pts.append(extended[i]) |
486 |
| - |
487 |
| - # create the faces |
488 |
| - for i in range(len(extended)): |
489 |
| - if extended[i] != extended[i-1]: |
490 |
| - mkquad(surface, (loop[i-1], loop[i], j, (j if j > l else len(pts)) -1), g) |
491 |
| - j += 1 |
492 |
| - else: |
493 |
| - mktri(surface, (loop[i-1], loop[i], (j if j > l else len(pts)) -1), g) |
494 |
| - |
495 |
| - return surface |
496 |
| - |
497 |
| -def axis_midpoint(a0: Axis, a1: Axis, x=0.5) -> vec3: |
498 |
| - ''' Return the midpoint of two axis. |
499 |
| - `x` is the blending factor between `a0` and `a1` |
500 |
| - |
501 |
| - - `x = 0` gives the point of `a0` the closest to `a1` |
502 |
| - - `x = 1` gives the point of `a1` the closest to `a0` |
503 |
| - ''' |
504 |
| - p0, d0 = a0 |
505 |
| - p1, d1 = a1 |
506 |
| - if dot(d0,d1)**2 == length2(d0)*length2(d1): |
507 |
| - return mix(p0, p1, 0.5) |
508 |
| - return mix( |
509 |
| - p0 + unproject(project(p1-p0, noproject(d0, d1)), d0), |
510 |
| - p1 + unproject(project(p0-p1, noproject(d1, d0)), d1), |
511 |
| - x) |
512 | 321 |
|
513 | 322 |
|
514 | 323 | # --- filling things ---
|
|
0 commit comments