[Closed] Help with cutting hole in 2D mesh polygon
Hi!
I need some help / advice on cutting holes in 2D mesh polygons, where the hole verts/edges is known and is on the same plane as the 2D mesh.
I got a simpleObject plugin that is creating a mesh from Truetype text. I detect hole figures/shapes with a brute force method that checks if shape startpoint is inside a previously defined polygon. The attached plugin require .NET Framework 3.0
The attached sample code is not optimized, lacks proper error handling, may contain memory-leaks, it lacks support for curve-interpration and may need some other work on, but it should work as a demonstration for my problem. I use 3dsmax 9 SP2
I am now just ignoring the holes and dismisses them with a meshop.deleteIsoVerts command.
What I need help with is help/advises/suggestions or ideas on how to cut holes in one polygon using another polygon (or vert positions). For example the letter O is 2 figures, I need to boolean out the inner polygon from the outer one.
I have explicit reasons for using simpleOBject, and other reasons for not using Shape-nodes or 3dsmax builtin Text objects. I also have reasons for not using builtin Boolean operations. That said, I need help on creating my own cutting algorithm.
My intention is not to cut the mesh after it is extruded, but cutting it when creating the 2D mesh
I have one idea:
- Iterate hole”-verts, check what face the vert is inside, divide that face
- Iterate hole”-verts again, cutting edges where no edge is between the verts
- Somehow select all faces inside hole edges, and delete the faces.
But I am not sure how I will execute that properly, of if it works in all shapes.
Any help is appreciated, thanks in advance! // Peter
/*
TTF to mesh
version 0.1
Last Edited: 26/03/2010
by Peter Larsen 2010
This plugin requires .Net framework 3.0.
Tested using 3dsmax v9 SP2
This sourcecode is Free to use in any way you may want to. Use this at your own risk.
History: v0.1 This version lacks feature to cut holes in the mesh shapes.
Code is not optimized and poor error handling routines.
Fontlist collector is not dotnet, but regedit hack.
Todo: Cut holes in geometry instead of ignoring those shapes.
Optimized code, better error handling.
Collecting fonts using dotnet classes.
Interpolating curves.
*/
plugin simpleObject LarsenTtfToMesh_plugin_def
name:"Ttf To Mesh"
classID:#(666666,888888)
category:"Scripted Primitives"
(
local ttfonts = #()
local ttfverts = #()
local ttffaces = #()
local createFontList, ttftomesh, updateText
parameters ttftmeshParams rollout:ROttftmesh
(
TTFToMesh_height type:#worldUnits ui:TTFToMesh_height default:0 animatable:true
TTFToMesh_width type:#worldUnits ui:TTFToMesh_width default:0 animatable:true
TTFToMesh_depth type:#worldUnits ui:TTFToMesh_depth default:0 animatable:true
selfont type:#string ui:lbttFonts animatable:true
seltext type:#string ui:ttfText default:"Test" animatable:true
)
rollout params "About" rolledUp:true
(
label about1 "\"TTF to mesh\""
label about2 "v0.1"
label about3 "by Peter Larsen 2010"
)
rollout ROttftmesh "Main Parameters" rolledUp:false
(
spinner TTFToMesh_height "Height" type:#worldunits range:[-1000,1000,0]
spinner TTFToMesh_width "Width" type:#worldunits range:[-1000,1000,0]
spinner TTFToMesh_depth "Depth" type:#worldunits range:[-1000,1000,0]
group "Text and font"
(
dropdownlist lbttFonts "Font"
edittext ttfText "Text :" fieldWidth:100 labelOnTop:true text: "Test"
)
on ttfText changed newStr do
(
updateText()
) -- on ttfText changed state
on lbttFonts selected sel do
(
updateText()
) -- on ttfText changed state
on ROttftmesh open do
(
setWaitCursor()
-- fontlist
if ttfonts.count == 0 do
createFontList ttfonts
if ttfonts.count != 0 do
if lbttFonts.items.count == 0 do
lbttFonts.items = ttfonts
updateText()
setArrowCursor()
) -- on ROttftmesh open
) -- rollout ROttftmesh
on buildMesh do
(
disableSceneRedraw()
suspendEditing()
local vertsBx = #()
undo off
(
if autosave.Enable do autosave.resettimer() -- reset AS timer (just for safe)
setWaitCursor()
local timeStart = timestamp()
for v = 1 to ttfverts.count do
(
-- set size
vertsBx [v] = [ ttfverts[v][1] * TTFToMesh_width,
ttfverts[v][2] * TTFToMesh_depth,
ttfverts[v][3] * TTFToMesh_height ]
)
-- create mesh
setMesh mesh \
verts: vertsBx \
faces: ttffaces
meshop.extrudeFaces mesh #{1..(ttffaces.count)} (TTFToMesh_height) 0 dir:#common
update mesh
local timeEnd = timestamp()
setArrowCursor()
format "
Total Time: % sec.
" ((timeEnd - timeStart)/1000.0)
)
resumeEditing()
enableSceneRedraw()
redrawviews()
gc()
)
tool create
(
on mousePoint click do
(
case click of
(
1: nodeTM.translation = gridPoint
3: #stop
)
) -- on mousePoint click
on mouseMove click do
(
case click of
(
2: (TTFToMesh_width = gridDist.x; TTFToMesh_depth = gridDist.y)
3: TTFToMesh_height = gridDist.z
)
) -- on mouseMove click
) -- tool create
function updateText =
(
-- get font and text
sel = ROttftmesh.lbttFonts.selection
if(sel >0) do
(
selfont = ROttftmesh.lbttFonts.items[sel]
seltext = ROttftmesh.ttfText.text
ttfverts = #()
ttffaces = #()
if (seltext.count > 0) do
ttftomesh ttfverts ttffaces selfont seltext
)
)
fn existFile fname = (getfiles fname).count != 0
function createFontList ttfonts =
(
-- directories
tmppath= (Getdir #temp ) + "\ tfonts\ ruetype.txt"
tmppath2= (Getdir #temp ) + "\ tfonts\\ascii_tt.txt"
-- Check
if ((existFile tmppath) == false) do
(
makeDir ((Getdir #temp) + "\ tfonts")
cmdstr = "regedit /e \"" + tmppath + "\" \"HKEY_LOCAL_MACHINE\software\Microsoft\Windows NT\CurrentVersion\Fonts\""
DOSCommand cmdstr
-- Unicode to ascii
cmdstr = "type \"" + tmppath + "\" > \"" + tmppath2 + "\""
DOSCommand cmdstr
)
if ttfonts.count == 0 do
(
-- load ascii_tt.txt
ttffs = openFile tmppath2
if(ttffs != undefined) then
(
tmpStr = ""
oldtmpStr = ""
while not eof ttffs do
(
oldtmpStr = copy tmpStr
tmpStr = readDelimitedString ttffs "\""
if(tmpStr[1] == "=") do
(
tmpfname_arr = filterString oldtmpStr "("
tmpfnametr = trimRight tmpfname_arr[1]
append ttfonts (copy tmpfnametr)
)
)
sort ttfonts
close ttffs
)
)
) -- function createFontList
fn isInside obj testpoint =
(
local sum = 0
local f = 1
numFaces = meshop.getNumFaces obj
-- loop all faces, if 360 then point is inside face
while ( (sum < 350) and (f <= numFaces) ) do
(
-- Get 3 edges
local faceEdges = (meshop.getEdgesUsingFace obj f)
faceEdges = faceEdges as array
sum = 0
-- Loop 3 edges
for i = 1 to faceEdges.count do
(
-- Get edge verts
local edgeVerts = meshop.getVertsUsingEdge obj faceEdges[i]
edgeVerts = edgeVerts as array
-- Get verts position and normalize vectors to testpoint
local n1 = normalize ( (meshop.getVert obj edgeVerts[1]) - testpoint)
local n2 = normalize ( (meshop.getVert obj edgeVerts[2]) - testpoint)
-- sum vectors
sum += acos (dot n1 n2)
)
f += 1
)
-- if 360 then then point is inside
if(sum>=350) then
return true
else
return false
)
fn getSeg verts w h figSeg curSp knoti corner =
(
-- Get Point
segPoint = figSeg.Point
append verts ([segPoint.x/w, segPoint.y/h, 0])
) -- fn getSeg
fn getPolySeg verts w h figSeg curSp knoti line corner =
(
-- Get Point Collection
segPointCollection = figSeg.Points
points = segPointCollection.CloneCurrentValue()
-- Count, Enumerator
pointCount = points.count
pointEnum = points.GetEnumerator()
for iter_points = 1 to pointCount do
(
-- Get current Point
pointEnum.MoveNext()
segPoint = pointEnum.Current
append verts ([segPoint.x/w, segPoint.y/h, 0])
) -- iter_points
) -- fn getPolySeg
function ttftomesh newverts newfaces sfont stext=
(
-- loading dotnet assemblies
dotnet.loadassembly "PresentationCore"
dotnet.loadassembly "System"
-- classes
geoClass = dotNetClass "System.Windows.Media.PathGeometry"
-- Init FormattedText arguments
txClass = dotNetClass "System.String"
txObj = dotNetObject txClass stext
fsClass = dotNetClass "System.Windows.FontStyle"
fsObj = dotNetObject fsClass (dotNetClass "FontStyles.Normal")
fwClass = dotNetClass "System.Windows.FontWeight"
fwObj = dotNetObject fwClass (dotNetClass "FontWeights.Medium")
ciClass = dotNetClass "System.Globalization.CultureInfo"
ciObj = dotNetObject ciClass "en-us"
ciObj2 = ciObj.GetCultureInfo "en-us"
fdClass = dotNetClass "System.Windows.FlowDirection"
fdObj = fdClass.LeftToRight
tfClass = dotNetClass "System.Windows.Media.Typeface"
tfObj = dotNetObject tfClass sfont
siObj = dotNetObject "System.Double" 32
brClass = dotNetClass "System.Windows.Media.SolidColorBrush"
brObj = dotNetObject brClass
ftClass = dotNetClass "System.Windows.Media.FormattedText"
-- Create FormattedText instance
ftObj = dotNetObject ftClass txObj ciOBj2 fdObj tfObj siObj brObj
-- Position Point class and Object
ptClass = dotNetClass "System.Windows.Point"
ptObj = dotNetObject ptClass 0 0
-- Call BuildGeometry Method, returns Geometry
geoObj = ftObj.BuildGeometry ptObj
-- Get Bounding Bounds
boundsRect = getProperty geoObj #Bounds asDotNetObject:true
-- Get width and height from Rect
w = getProperty boundsRect #Width
h = getProperty boundsRect #Height
-- Get path
geoPath = geoObj.GetOutlinedPathGeometry()
-- Get figures
geoPathClone = geoPath.CloneCurrentValue()
geoFigures = getProperty geoPathClone #Figures asDotNetObject:true
geoFillrule = getProperty geoPathClone #Fillrule asDotNetObject:true
-- Get Figures count
geoFigCount = geoFigures.Count
-- Get Figure Item
figEnum = geoFigures.GetEnumerator()
-- Variables
verts = #()
polys = #()
for iter_figs = 1 to geoFigCount do
(
figEnum.MoveNext()
geoFig0 = figEnum.Current
-- Get a Figure startpoint "System.Windows.Point"
geoStartPoint = geoFig0.startpoint
-- isclosed
figIsClosed = geoFig0.IsClosed
-- isFilled
figIsFilled = geoFig0.IsFilled
append verts ([geoStartPoint.x / w, geoStartPoint.y / h, 0])
append polys verts.count
-- Get Segments Collection
geoPathFigureSegCollection = geoFig0.Segments
segs = geoPathFigureSegCollection.CloneCurrentValue()
segsCount = segs.count
segsEnum = segs.GetEnumerator()
for iter_segs = 1 to segsCount do
(
-- Get current Segment
segsEnum.MoveNext()
figSeg = segsEnum.Current
-- Get Segment Type
segStr = figSeg.ToString()
-- IsSmoothJoin
IsSmoothJoin = figSeg.IsSmoothJoin
-- IsStroked
IsStroked = figSeg.IsStroked
if (segStr == "System.Windows.Media.LineSegment") do (getSeg verts w h figSeg curSp knoti IsSmoothJoin)
if (segStr == "System.Windows.Media.PolyLineSegment") do (getPolySeg verts w h figSeg curSp knoti true IsSmoothJoin )
if (segStr == "System.Windows.Media.PolyBezierSegment") do (getPolySeg verts w h figSeg curSp knoti false IsSmoothJoin )
) -- iter_segs
append polys verts.count
) -- iter_figs
undo off
(
newmesh = Trimesh()
setmesh newmesh vertices:verts faces:#() -- materialIDS:#(1,2)
for p = (polys.count-1) to 1 by -2 do
(
vertnumberarr = #{polys[p]..polys[p+1]} as array
-- Check if startpoint is inside previous poly
if(p<(polys.count-1)) then
(
startpoint = meshop.getvert newmesh vertnumberarr[1]
if ( (isInside newmesh startpoint) == false ) then
meshop.createPolygon newmesh vertnumberarr
) else meshop.createPolygon newmesh vertnumberarr
update newmesh
)
meshop.flipNormals newmesh #all
update newmesh
)
-- remove isolated verts
meshop.deleteIsoVerts newmesh
-- get verts
num_verts = newmesh.numverts
for v = 1 to num_verts do
append newverts (getVert newmesh v)
-- copy verts
newverts = verts
-- get faces
num_faces = newmesh.numfaces
for f = 1 to num_faces do
append newfaces (getFace newmesh f)
-- Garbage Collection
dotNetGC = dotnetclass "System.GC"
dotNetGC.collect()
)
)
Probably you have your own reason to make it your way but using MAX you can do it easier:
- Create Text Shape
- Apply Shell modifier
- Convert To … if necessary
PS. Times Roman (compatible with Times New Roman), Helvetica (compatible with Arial) and Courier (compatible with Courier New)
Hi denis!
Yes, that is true. I can do it that way manually, or creating a standard plugin for it. But I have my reasons. One is that that I already have an extensive SimpleObject plugin (not the sample one I attached) that I need the Truetype functionality into, and a simpleObject does not cooperate well with creating shape-nodes and “CreateInstance Text” does not leave you with the option to convert it to a mesh.
Another reason is that I in the future want more control over the “splineshape” to “mesh” conversion, so I can apply my own features as I want them.
I am currently working on my idea on how to cut the polygons, and will post it as soon as the prototype is finnished, perhaps someone could help me sort it out to function better, and somewhat optimize it
Updated version with first attempt at cutting polys, see function: cutHole. It does not perform well, and I fail to find what goes wrong
/*
TTF to mesh
version 0.1.1
Last Edited: 28/03/2010
by Peter Larsen 2010
This plugin requires .Net framework 3.0.
Tested using 3dsmax v9 SP2
This sourcecode is Free to use in any way you may want to. Use this at your own risk.
History: v1.1.1 First attempt on creating a hole cutting function: "cutHole"
v0.1 This version lacks feature to cut holes in the mesh shapes.
Code is not optimized and poor error handling routines.
Fontlist collector is not dotnet, but regedit hack.
Todo: Cut proper holes in geometry instead of crazy holes.
Optimized code, better error handling.
Collecting fonts using dotnet classes.
Interpolating curves.
*/
plugin simpleObject LarsenTtfToMesh_plugin_def
name:"TtfToMesh"
classID:#(666666,888888)
category:"Scripted Primitives"
(
local ttfonts = #()
local ttfverts = #()
local ttffaces = #()
local createFontList, ttftomesh, updateText, isInside
parameters ttftmeshParams rollout:ROttftmesh
(
TTFToMesh_height type:#worldUnits ui:TTFToMesh_height default:0 animatable:true
TTFToMesh_width type:#worldUnits ui:TTFToMesh_width default:0 animatable:true
TTFToMesh_depth type:#worldUnits ui:TTFToMesh_depth default:0 animatable:true
selfont type:#string ui:lbttFonts animatable:true
seltext type:#string ui:ttfText default:"Test" animatable:true
cbCutHoles type:#boolean ui:cbCutHoles animatable:true
)
rollout params "About" rolledUp:true
(
label about1 "\"TTF to mesh\""
label about2 "v0.1"
label about3 "by Peter Larsen 2010"
)
rollout ROttftmesh "Main Parameters" rolledUp:false
(
spinner TTFToMesh_height "Height" type:#worldunits range:[-1000,1000,0]
spinner TTFToMesh_width "Width" type:#worldunits range:[-1000,1000,0]
spinner TTFToMesh_depth "Depth" type:#worldunits range:[-1000,1000,0]
group "Text and font"
(
dropdownlist lbttFonts "Font"
edittext ttfText "Text :" fieldWidth:100 labelOnTop:true text: "Test"
)
checkbox cbCutHoles "Cut Holes" checked:false triState:0
on ttfText changed newStr do
(
updateText()
) -- on ttfText changed state
on lbttFonts selected sel do
(
updateText()
) -- on ttfText changed state
on cbCutHoles changed state do
(
updateText()
) -- on ttfText changed state
on ROttftmesh open do
(
setWaitCursor()
-- fontlist
if ttfonts.count == 0 do
createFontList ttfonts
if ttfonts.count != 0 do
if lbttFonts.items.count == 0 do
lbttFonts.items = ttfonts
updateText()
setArrowCursor()
) -- on ROttftmesh open
) -- rollout ROttftmesh
on buildMesh do
(
disableSceneRedraw()
suspendEditing()
local vertsBx = #()
undo off
(
if autosave.Enable do autosave.resettimer() -- reset AS timer (just for safe)
setWaitCursor()
local timeStart = timestamp()
if( (ttfverts.count==0) or (ttffaces.count==0) ) then
(
-- create empty dummy
setMesh mesh \
verts: #([0,0,0], [TTFToMesh_width, 0, 0], [TTFToMesh_width, TTFToMesh_depth, TTFToMesh_height]) \
faces: #([3,2,1])
) else (
for v = 1 to ttfverts.count do
(
-- set size
vertsBx [v] = [ ttfverts[v][1] * TTFToMesh_width,
ttfverts[v][2] * TTFToMesh_depth,
ttfverts[v][3] * TTFToMesh_height ]
)
-- create mesh
setMesh mesh \
verts: vertsBx \
faces: ttffaces
)
meshop.extrudeFaces mesh #{1..(ttffaces.count)} (TTFToMesh_height) 0 dir:#common
update mesh
local timeEnd = timestamp()
setArrowCursor()
format "
Total Time: % sec.
" ((timeEnd - timeStart)/1000.0)
)
resumeEditing()
enableSceneRedraw()
redrawviews()
gc()
)
tool create
(
on mousePoint click do
(
case click of
(
1: nodeTM.translation = gridPoint
3: #stop
)
) -- on mousePoint click
on mouseMove click do
(
case click of
(
2: (TTFToMesh_width = gridDist.x; TTFToMesh_depth = gridDist.y)
3: TTFToMesh_height = gridDist.z
)
) -- on mouseMove click
) -- tool create
function updateText =
(
-- get font and text
sel = ROttftmesh.lbttFonts.selection
if(sel >0) do
(
selfont = ROttftmesh.lbttFonts.items[sel]
seltext = ROttftmesh.ttfText.text
ttfverts = #()
ttffaces = #()
if (seltext.count > 0) do
ttftomesh ttfverts ttffaces selfont seltext
)
)
fn existFile fname = (getfiles fname).count != 0
function createFontList ttfonts =
(
-- directories
tmppath= (Getdir #temp ) + "\ tfonts\ ruetype.txt"
tmppath2= (Getdir #temp ) + "\ tfonts\\ascii_tt.txt"
-- Check
if ((existFile tmppath) == false) do
(
makeDir ((Getdir #temp) + "\ tfonts")
cmdstr = "regedit /e \"" + tmppath + "\" \"HKEY_LOCAL_MACHINE\software\Microsoft\Windows NT\CurrentVersion\Fonts\""
DOSCommand cmdstr
-- Unicode to ascii
cmdstr = "type \"" + tmppath + "\" > \"" + tmppath2 + "\""
DOSCommand cmdstr
)
if ttfonts.count == 0 do
(
-- load ascii_tt.txt
ttffs = openFile tmppath2
if(ttffs != undefined) then
(
tmpStr = ""
oldtmpStr = ""
while not eof ttffs do
(
oldtmpStr = copy tmpStr
tmpStr = readDelimitedString ttffs "\""
if(tmpStr[1] == "=") do
(
tmpfname_arr = filterString oldtmpStr "("
tmpfnametr = trimRight tmpfname_arr[1]
append ttfonts (copy tmpfnametr)
)
)
sort ttfonts
close ttffs
)
)
) -- function createFontList
fn cutHole newmesh vertnumberarr vertnumberbitarray removefaces =
(
-- 1. Iterate hole"-verts, check what face the vert is inside, divide that face
-- 2. Iterate hole"-verts again, cutting edges where no edge is between the verts
-- 3. Somehow select all faces inside hole edges, and delete the faces.
-- add faces to mesh
numVerts = meshop.getNumVerts newmesh
numFaces = meshop.getNumFaces newmesh
vertCount = vertnumberarr.count
local newEdges = #()
local holeverts = #()
local cutNormal = getFaceNormal newmesh 1
-- 1: iterate and split faces
for hv = 1 to vertCount do
(
holevert = vertnumberarr[hv]
holepoint = meshop.getvert newmesh holevert
-- find face
f = isinside true newmesh holepoint
-- divide face on holepoint
-- get the original face verts
if(f>0) do
(
numVerts += 1
append holeverts numVerts
origfverts = getFace newmesh f
-- get bary
bary = meshop.getBaryCoords newmesh f holepoint
-- divide face
meshop.divideFace newmesh f baryCoord:bary
update newmesh
-- store created edges for later use
vertEdges = meshop.getEdgesUsingVert newmesh #{numVerts}
vertEdgesArr = vertEdges as array
faceEdges = meshop.getEdgesUsingFace newmesh #{f}
append newedges ((vertEdges * faceEdges ) as array )
)
update newmesh
)
-- 2: cut where no edge exists
for he = 1 to newEdges.count do
(
he2 = he+1
if(he2 > newEdges.count) do he2=1
-- check that edge isn't already between verts
holevert1 = holeverts[he]
holevert2 = holeverts[he2]
holevert1edges = ((meshop.getEdgesUsingVert newmesh #(holevert1) ) as array)
holevert2edges = ((meshop.getEdgesUsingVert newmesh #(holevert2) ) as array)
-- iterate edges, to make sure they already do not share any edge
edgeexists = false
for e1 = 1 to holevert1edges.count do
for e2 = 1 to holevert2edges.count do
if ( holevert1edges[e1] == holevert2edges[e2] ) do
edgeexists = true
-- if there is no edge, cut!
if ( edgeexists == false ) do
try(
-- get the edges
edge1 = newEdges[he][2]
edge2 = newEdges[he2][2]
if (edge2 == edge1) do
edge2 = newEdges[he2][1]
--meshop.turnEdge newmesh edge1
meshop.cut newmesh edge1 (0.0) edge2 (0.0) (cutNormal) fixNeighbors:false split:true
update newmesh
) catch ()
) -- for he = 1 to newEdges.count do
-- 3: remove inner faces
holevertfaces = (meshop.getFacesUsingVert newmesh vertnumberarr) as array
for hf = 1 to holevertfaces.count do
(
-- find faces where all verts is holeverts (vertnumberbitarray)
faceverts = (meshop.getVertsUsingFace newmesh #(holevertfaces[hf])) as array
innerface = true
for fv = 1 to faceverts.count do
(
vertindex = faceverts[fv]
if (vertnumberbitarray[vertindex] == false) do
innerface = false
)
-- if innerface, add to removeface array
if (innerface == true) do
append removefaces holevertfaces[hf]
)
) -- fn cutHole newmesh vertnumberarr
fn isInside getface obj testpoint =
(
local sum = 0
local f = 1
numFaces = meshop.getNumFaces obj
-- loop all faces, if 360 then point is inside face
while ( (sum < 350) and (f <= numFaces) ) do
(
-- Get 3 edges
local faceEdges = (meshop.getEdgesUsingFace obj f)
faceEdges = faceEdges as array
sum = 0
-- Loop 3 edges
for i = 1 to faceEdges.count do
(
-- Get edge verts
local edgeVerts = meshop.getVertsUsingEdge obj faceEdges[i]
edgeVerts = edgeVerts as array
-- Get verts position and normalize vectors to testpoint
if(edgeVerts.count == 2) do
(
local n1 = normalize ( (meshop.getVert obj edgeVerts[1]) - testpoint)
local n2 = normalize ( (meshop.getVert obj edgeVerts[2]) - testpoint)
-- sum vectors
sum += acos (dot n1 n2)
)
)
f += 1
)
-- if 360 then then point is inside
if ( getface == true) then
if (sum>=350) then
return (f-1)
else
return 0
else if(sum>=350) then
return true
else
return false
)
fn getSeg verts w h figSeg curSp knoti corner =
(
-- Get Point
segPoint = figSeg.Point
append verts ([segPoint.x/w, segPoint.y/h, 0])
) -- fn getSeg
fn getPolySeg verts w h figSeg curSp knoti line corner =
(
-- Get Point Collection
segPointCollection = figSeg.Points
points = segPointCollection.CloneCurrentValue()
-- Count, Enumerator
pointCount = points.count
pointEnum = points.GetEnumerator()
for iter_points = 1 to pointCount do
(
-- Get current Point
pointEnum.MoveNext()
segPoint = pointEnum.Current
append verts ([segPoint.x/w, segPoint.y/h, 0])
) -- iter_points
) -- fn getPolySeg
function ttftomesh newverts newfaces sfont stext=
(
-- loading dotnet assemblies
dotnet.loadassembly "PresentationCore"
dotnet.loadassembly "System"
-- classes
geoClass = dotNetClass "System.Windows.Media.PathGeometry"
-- Init FormattedText arguments
txClass = dotNetClass "System.String"
txObj = dotNetObject txClass stext
fsClass = dotNetClass "System.Windows.FontStyle"
fsObj = dotNetObject fsClass (dotNetClass "FontStyles.Normal")
fwClass = dotNetClass "System.Windows.FontWeight"
fwObj = dotNetObject fwClass (dotNetClass "FontWeights.Medium")
ciClass = dotNetClass "System.Globalization.CultureInfo"
ciObj = dotNetObject ciClass "en-us"
ciObj2 = ciObj.GetCultureInfo "en-us"
fdClass = dotNetClass "System.Windows.FlowDirection"
fdObj = fdClass.LeftToRight
tfClass = dotNetClass "System.Windows.Media.Typeface"
tfObj = dotNetObject tfClass sfont
siObj = dotNetObject "System.Double" 32
brClass = dotNetClass "System.Windows.Media.SolidColorBrush"
brObj = dotNetObject brClass
ftClass = dotNetClass "System.Windows.Media.FormattedText"
-- Create FormattedText instance
ftObj = dotNetObject ftClass txObj ciOBj2 fdObj tfObj siObj brObj
-- Position Point class and Object
ptClass = dotNetClass "System.Windows.Point"
ptObj = dotNetObject ptClass 0 0
-- Call BuildGeometry Method, returns Geometry
geoObj = ftObj.BuildGeometry ptObj
-- Get Bounding Bounds
boundsRect = getProperty geoObj #Bounds asDotNetObject:true
-- Get width and height from Rect
w = getProperty boundsRect #Width
h = getProperty boundsRect #Height
-- Get path
geoPath = geoObj.GetOutlinedPathGeometry()
-- Get figures
geoPathClone = geoPath.CloneCurrentValue()
geoFigures = getProperty geoPathClone #Figures asDotNetObject:true
geoFillrule = getProperty geoPathClone #Fillrule asDotNetObject:true
-- Get Figures count
geoFigCount = geoFigures.Count
-- Get Figure Item
figEnum = geoFigures.GetEnumerator()
-- Variables
verts = #()
polys = #()
for iter_figs = 1 to geoFigCount do
(
figEnum.MoveNext()
geoFig0 = figEnum.Current
-- Get a Figure startpoint "System.Windows.Point"
geoStartPoint = geoFig0.startpoint
-- isclosed
figIsClosed = geoFig0.IsClosed
-- isFilled
figIsFilled = geoFig0.IsFilled
append verts ([geoStartPoint.x / w, geoStartPoint.y / h, 0])
append polys verts.count
-- Get Segments Collection
geoPathFigureSegCollection = geoFig0.Segments
segs = geoPathFigureSegCollection.CloneCurrentValue()
segsCount = segs.count
segsEnum = segs.GetEnumerator()
for iter_segs = 1 to segsCount do
(
-- Get current Segment
segsEnum.MoveNext()
figSeg = segsEnum.Current
-- Get Segment Type
segStr = figSeg.ToString()
-- IsSmoothJoin
IsSmoothJoin = figSeg.IsSmoothJoin
-- IsStroked
IsStroked = figSeg.IsStroked
if (segStr == "System.Windows.Media.LineSegment") do (getSeg verts w h figSeg curSp knoti IsSmoothJoin)
if (segStr == "System.Windows.Media.PolyLineSegment") do (getPolySeg verts w h figSeg curSp knoti true IsSmoothJoin )
if (segStr == "System.Windows.Media.PolyBezierSegment") do (getPolySeg verts w h figSeg curSp knoti false IsSmoothJoin )
) -- iter_segs
append polys verts.count
) -- iter_figs
holepolyverts = #()
undo off
(
newmesh = Trimesh()
setmesh newmesh vertices:verts faces:#() -- materialIDS:#(1,2)
for p = (polys.count-1) to 1 by -2 do
(
vertnumberbitarray = #{polys[p]..polys[p+1]}
vertnumberarr = vertnumberbitarray as array
-- Check if startpoint is inside previous poly
if(p<(polys.count-1)) then
(
startpoint = meshop.getvert newmesh vertnumberarr[1]
if ( (isInside false newmesh startpoint) == false ) then
(
meshop.createPolygon newmesh vertnumberarr
) else (
append holepolyverts polys[p]
append holepolyverts polys[p+1]
)
) else meshop.createPolygon newmesh vertnumberarr
update newmesh
)
-- cut holes
removefaces = #()
if (ROttftmesh.cbCutHoles.checked == true) do
for p = 1 to holepolyverts.count by 2 do
(
vertnumberbitarray = #{holepolyverts[p]..holepolyverts[p+1]}
vertnumberarr = vertnumberbitarray as array
cutHole newmesh vertnumberarr vertnumberbitarray removefaces
)
if(removefaces.count > 0) do
meshop.deleteFaces newmesh removefaces delIsoVerts:false
meshop.removeDegenerateFaces newmesh
meshop.removeIllegalFaces newmesh
meshop.flipNormals newmesh #all
update newmesh
) -- undo off
-- remove isolated verts
meshop.deleteIsoVerts newmesh
update newmesh
-- get verts
num_verts = newmesh.numverts
for v = 1 to num_verts do
append newverts (getVert newmesh v)
-- copy verts
newverts = verts
-- get faces
num_faces = newmesh.numfaces
for f = 1 to num_faces do
append newfaces (getFace newmesh f)
-- Garbage Collection
dotNetGC = dotnetclass "System.GC"
dotNetGC.collect()
) -- function ttftomesh newverts newfaces sfont stext
)
I think I go try this solution: Ear-clipping of polygons
Does anyone have maxscript code for that type of algorithm ?
Some progress is achieved, but still not there, look at attached image, see the “PH” letters for example and you see my problem.
If someone can find the bug/bugs in my ugly code I would be very happy! because it is giving me headache